Imported Upstream version 1.40.0 21/225721/1 upstream/1.40.0
authorSeonah Moon <seonah1.moon@samsung.com>
Mon, 24 Feb 2020 06:50:50 +0000 (15:50 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Mon, 24 Feb 2020 06:51:10 +0000 (15:51 +0900)
Change-Id: I9c0c152accecc19b480784589682be44a61e4ce4

388 files changed:
AUTHORS
CMakeLists.txt
CMakeOptions.txt
ChangeLog
Makefile.in
README.rst
aclocal.m4
cmake/FindLibevent.cmake
config.h.in
configure
configure.ac
contrib/Makefile.in
doc/Makefile.am
doc/Makefile.in
doc/bash_completion/h2load
doc/h2load.1
doc/h2load.1.rst
doc/nghttp.1
doc/nghttp2_check_authority.rst [new file with mode: 0644]
doc/nghttp2_option_set_max_outbound_ack.rst [new file with mode: 0644]
doc/nghttpd.1
doc/nghttpx.1
doc/nghttpx.1.rst
doc/sources/contribute.rst
doc/sources/nghttpx-howto.rst
examples/CMakeLists.txt
examples/Makefile.am
examples/Makefile.in
examples/libevent-client.c
integration-tests/Makefile.am
integration-tests/Makefile.in
integration-tests/nghttpx_http1_test.go
integration-tests/server_tester.go
lib/CMakeLists.txt
lib/Makefile.in
lib/includes/Makefile.in
lib/includes/nghttp2/nghttp2.h
lib/includes/nghttp2/nghttp2ver.h
lib/nghttp2_hd.c
lib/nghttp2_hd.h
lib/nghttp2_hd_huffman.c
lib/nghttp2_hd_huffman.h
lib/nghttp2_hd_huffman_data.c
lib/nghttp2_helper.c
lib/nghttp2_http.c
lib/nghttp2_option.c
lib/nghttp2_option.h
lib/nghttp2_session.c
lib/nghttp2_session.h
lib/nghttp2_stream.c
lib/nghttp2_stream.h
ltmain.sh
m4/ax_cxx_compile_stdcxx.m4 [new file with mode: 0644]
m4/ax_cxx_compile_stdcxx_11.m4 [deleted file]
m4/libtool.m4
python/Makefile.in
script/Makefile.in
src/CMakeLists.txt
src/HttpServer.cc
src/Makefile.am
src/Makefile.in
src/allocator.h
src/asio_client_request.cc
src/asio_client_response.cc
src/asio_client_session.cc
src/asio_client_session_impl.cc
src/asio_server.cc
src/asio_server.h
src/asio_server_connection.h
src/asio_server_http2.cc
src/asio_server_http2_handler.cc
src/asio_server_http2_impl.cc
src/asio_server_http2_impl.h
src/asio_server_request.cc
src/asio_server_response.cc
src/h2load.cc
src/h2load.h
src/h2load_http1_session.cc
src/h2load_http1_session.h
src/http2.cc
src/http2.h
src/http2_test.cc
src/includes/Makefile.in
src/includes/nghttp2/asio_http2_client.h
src/includes/nghttp2/asio_http2_server.h
src/memchunk.h
src/memchunk_test.cc
src/nghttp.cc
src/nghttp.h
src/nghttpd.cc
src/shrpx.cc
src/shrpx_api_downstream_connection.cc
src/shrpx_api_downstream_connection.h
src/shrpx_client_handler.cc
src/shrpx_client_handler.h
src/shrpx_config.cc
src/shrpx_config.h
src/shrpx_config_test.cc
src/shrpx_connect_blocker.cc
src/shrpx_connection.cc
src/shrpx_connection.h
src/shrpx_connection_handler.cc
src/shrpx_connection_handler.h
src/shrpx_dns_resolver.cc
src/shrpx_dns_resolver.h
src/shrpx_dns_tracker.cc
src/shrpx_dns_tracker.h
src/shrpx_downstream.cc
src/shrpx_downstream.h
src/shrpx_downstream_queue.cc
src/shrpx_downstream_queue.h
src/shrpx_dual_dns_resolver.cc
src/shrpx_dual_dns_resolver.h
src/shrpx_http.cc
src/shrpx_http.h
src/shrpx_http2_downstream_connection.cc
src/shrpx_http2_session.cc
src/shrpx_http2_session.h
src/shrpx_http2_upstream.cc
src/shrpx_http_downstream_connection.cc
src/shrpx_http_downstream_connection.h
src/shrpx_https_upstream.cc
src/shrpx_https_upstream.h
src/shrpx_live_check.cc
src/shrpx_log.cc
src/shrpx_log.h
src/shrpx_log_config.cc
src/shrpx_memcached_connection.cc
src/shrpx_memcached_connection.h
src/shrpx_memcached_dispatcher.cc
src/shrpx_memcached_request.h
src/shrpx_memcached_result.h
src/shrpx_mruby.cc
src/shrpx_mruby_module_response.cc
src/shrpx_router.cc
src/shrpx_router_test.cc
src/shrpx_signal.cc
src/shrpx_tls.cc
src/shrpx_tls_test.cc
src/shrpx_worker.cc
src/shrpx_worker.h
src/shrpx_worker_process.cc
src/template.h
src/util.cc
src/util.h
tests/Makefile.in
tests/main.c
tests/nghttp2_hd_test.c
tests/nghttp2_hd_test.h
tests/nghttp2_session_test.c
tests/nghttp2_session_test.h
tests/testdata/Makefile.in
third-party/CMakeLists.txt
third-party/Makefile.am
third-party/Makefile.in
third-party/http-parser/http_parser.c [deleted file]
third-party/http-parser/http_parser.h [deleted file]
third-party/llhttp/include/llhttp.h [new file with mode: 0644]
third-party/llhttp/src/api.c [new file with mode: 0644]
third-party/llhttp/src/http.c [new file with mode: 0644]
third-party/llhttp/src/llhttp.c [new file with mode: 0644]
third-party/mruby/AUTHORS
third-party/mruby/LEGAL
third-party/mruby/LICENSE [moved from third-party/mruby/MITL with 96% similarity]
third-party/mruby/README.md
third-party/mruby/Rakefile
third-party/mruby/TODO
third-party/mruby/appveyor.yml
third-party/mruby/benchmark/bm_app_lc_fizzbuzz.rb
third-party/mruby/bin/.gitkeep [deleted file]
third-party/mruby/build_config.rb
third-party/mruby/doc/guides/debugger.md
third-party/mruby/doc/guides/mrbgems.md
third-party/mruby/doc/limitations.md
third-party/mruby/doc/opcode.md [new file with mode: 0644]
third-party/mruby/examples/targets/build_config_android_arm64-v8a.rb
third-party/mruby/examples/targets/build_config_android_armeabi.rb
third-party/mruby/include/mrbconf.h
third-party/mruby/include/mruby.h
third-party/mruby/include/mruby/array.h
third-party/mruby/include/mruby/boxing_nan.h
third-party/mruby/include/mruby/boxing_word.h
third-party/mruby/include/mruby/class.h
third-party/mruby/include/mruby/common.h
third-party/mruby/include/mruby/compile.h
third-party/mruby/include/mruby/data.h
third-party/mruby/include/mruby/debug.h
third-party/mruby/include/mruby/dump.h
third-party/mruby/include/mruby/error.h
third-party/mruby/include/mruby/hash.h
third-party/mruby/include/mruby/irep.h
third-party/mruby/include/mruby/object.h
third-party/mruby/include/mruby/opcode.h
third-party/mruby/include/mruby/ops.h [new file with mode: 0644]
third-party/mruby/include/mruby/proc.h
third-party/mruby/include/mruby/range.h
third-party/mruby/include/mruby/string.h
third-party/mruby/include/mruby/throw.h
third-party/mruby/include/mruby/value.h
third-party/mruby/include/mruby/variable.h
third-party/mruby/include/mruby/version.h
third-party/mruby/lib/mruby-core-ext.rb
third-party/mruby/lib/mruby/build.rb
third-party/mruby/lib/mruby/build/command.rb
third-party/mruby/lib/mruby/gem.rb
third-party/mruby/minirake
third-party/mruby/mrbgems/default.gembox
third-party/mruby/mrbgems/mruby-array-ext/mrblib/array.rb
third-party/mruby/mrbgems/mruby-array-ext/src/array.c
third-party/mruby/mrbgems/mruby-array-ext/test/array.rb
third-party/mruby/mrbgems/mruby-bin-config/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-bin-config/mruby-config [moved from third-party/mruby/mrbgems/mruby-bin-mruby-config/mruby-config with 100% similarity]
third-party/mruby/mrbgems/mruby-bin-config/mruby-config.bat [moved from third-party/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat with 100% similarity]
third-party/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
third-party/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb
third-party/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
third-party/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake
third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
third-party/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake [deleted file]
third-party/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb
third-party/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake
third-party/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
third-party/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
third-party/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
third-party/mruby/mrbgems/mruby-class-ext/mrblib/module.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-class-ext/test/module.rb
third-party/mruby/mrbgems/mruby-compiler/core/codegen.c
third-party/mruby/mrbgems/mruby-compiler/core/node.h
third-party/mruby/mrbgems/mruby-compiler/core/parse.y
third-party/mruby/mrbgems/mruby-compiler/mrbgem.rake
third-party/mruby/mrbgems/mruby-enum-chain/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-enum-chain/mrblib/chain.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-enum-chain/test/enum_chain.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb
third-party/mruby/mrbgems/mruby-enum-ext/test/enum.rb
third-party/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
third-party/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb
third-party/mruby/mrbgems/mruby-enumerator/test/enumerator.rb
third-party/mruby/mrbgems/mruby-eval/src/eval.c
third-party/mruby/mrbgems/mruby-eval/test/eval.rb
third-party/mruby/mrbgems/mruby-fiber/src/fiber.c
third-party/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb
third-party/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c
third-party/mruby/mrbgems/mruby-hash-ext/test/hash.rb
third-party/mruby/mrbgems/mruby-inline-struct/test/inline.c
third-party/mruby/mrbgems/mruby-inline-struct/test/inline.rb
third-party/mruby/mrbgems/mruby-io/mrbgem.rake
third-party/mruby/mrbgems/mruby-io/src/file.c
third-party/mruby/mrbgems/mruby-io/src/io.c
third-party/mruby/mrbgems/mruby-io/test/file.rb
third-party/mruby/mrbgems/mruby-io/test/file_test.rb
third-party/mruby/mrbgems/mruby-io/test/gc_filedes.sh [deleted file]
third-party/mruby/mrbgems/mruby-io/test/io.rb
third-party/mruby/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
third-party/mruby/mrbgems/mruby-kernel-ext/src/kernel.c
third-party/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb
third-party/mruby/mrbgems/mruby-math/src/math.c
third-party/mruby/mrbgems/mruby-math/test/math.rb
third-party/mruby/mrbgems/mruby-metaprog/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-metaprog/src/metaprog.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-metaprog/test/metaprog.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-method/mrblib/kernel.rb
third-party/mruby/mrbgems/mruby-method/mrblib/method.rb
third-party/mruby/mrbgems/mruby-method/src/method.c
third-party/mruby/mrbgems/mruby-method/test/method.rb
third-party/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c
third-party/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb
third-party/mruby/mrbgems/mruby-object-ext/test/nil.rb
third-party/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c
third-party/mruby/mrbgems/mruby-objectspace/test/objectspace.rb
third-party/mruby/mrbgems/mruby-pack/src/pack.c
third-party/mruby/mrbgems/mruby-pack/test/pack.rb
third-party/mruby/mrbgems/mruby-print/mrblib/print.rb
third-party/mruby/mrbgems/mruby-print/src/print.c
third-party/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb
third-party/mruby/mrbgems/mruby-proc-ext/src/proc.c
third-party/mruby/mrbgems/mruby-proc-ext/test/proc.rb
third-party/mruby/mrbgems/mruby-random/src/random.c
third-party/mruby/mrbgems/mruby-random/test/random.rb
third-party/mruby/mrbgems/mruby-range-ext/mrblib/range.rb
third-party/mruby/mrbgems/mruby-range-ext/src/range.c
third-party/mruby/mrbgems/mruby-sleep/.gitignore [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/.travis.yml [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/.travis_build_config.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/README.md [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/Rakefile [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/example/sleep.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/src/mrb_sleep.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sleep/test/sleep_test.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/mrbgem.rake
third-party/mruby/mrbgems/mruby-socket/src/socket.c
third-party/mruby/mrbgems/mruby-socket/test/addrinfo.rb
third-party/mruby/mrbgems/mruby-socket/test/socket.rb
third-party/mruby/mrbgems/mruby-socket/test/sockettest.c
third-party/mruby/mrbgems/mruby-sprintf/src/sprintf.c
third-party/mruby/mrbgems/mruby-sprintf/test/sprintf.rb
third-party/mruby/mrbgems/mruby-string-ext/mrblib/string.rb
third-party/mruby/mrbgems/mruby-string-ext/src/string.c
third-party/mruby/mrbgems/mruby-string-ext/test/string.rb
third-party/mruby/mrbgems/mruby-struct/mrblib/struct.rb
third-party/mruby/mrbgems/mruby-struct/src/struct.c
third-party/mruby/mrbgems/mruby-struct/test/struct.rb
third-party/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
third-party/mruby/mrbgems/mruby-symbol-ext/src/symbol.c
third-party/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb
third-party/mruby/mrbgems/mruby-test/driver.c
third-party/mruby/mrbgems/mruby-test/init_mrbtest.c [deleted file]
third-party/mruby/mrbgems/mruby-test/mrbgem.rake
third-party/mruby/mrbgems/mruby-time/include/mruby/time.h [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-time/src/time.c
third-party/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb
third-party/mruby/mrblib/array.rb
third-party/mruby/mrblib/enum.rb
third-party/mruby/mrblib/float.rb [deleted file]
third-party/mruby/mrblib/hash.rb
third-party/mruby/mrblib/kernel.rb
third-party/mruby/mrblib/mrblib.rake
third-party/mruby/mrblib/numeric.rb
third-party/mruby/mrblib/string.rb
third-party/mruby/mrblib/symbol.rb [new file with mode: 0644]
third-party/mruby/src/array.c
third-party/mruby/src/backtrace.c
third-party/mruby/src/class.c
third-party/mruby/src/codedump.c
third-party/mruby/src/debug.c
third-party/mruby/src/dump.c
third-party/mruby/src/enum.c
third-party/mruby/src/error.c
third-party/mruby/src/etc.c
third-party/mruby/src/ext/.gitkeep [deleted file]
third-party/mruby/src/fmt_fp.c
third-party/mruby/src/gc.c
third-party/mruby/src/hash.c
third-party/mruby/src/kernel.c
third-party/mruby/src/load.c
third-party/mruby/src/mruby_core.rake
third-party/mruby/src/numeric.c
third-party/mruby/src/object.c
third-party/mruby/src/proc.c
third-party/mruby/src/range.c
third-party/mruby/src/state.c
third-party/mruby/src/string.c
third-party/mruby/src/symbol.c
third-party/mruby/src/variable.c
third-party/mruby/src/vm.c
third-party/mruby/tasks/libmruby.rake
third-party/mruby/tasks/mrbgems.rake
third-party/mruby/tasks/toolchains/android.rake
third-party/mruby/tasks/toolchains/clang.rake
third-party/mruby/tasks/toolchains/gcc.rake
third-party/mruby/tasks/toolchains/openwrt.rake
third-party/mruby/tasks/toolchains/visualcpp.rake
third-party/mruby/test/assert.rb
third-party/mruby/test/bintest.rb
third-party/mruby/test/report.rb [deleted file]
third-party/mruby/test/t/array.rb
third-party/mruby/test/t/bs_block.rb
third-party/mruby/test/t/class.rb
third-party/mruby/test/t/codegen.rb
third-party/mruby/test/t/enumerable.rb
third-party/mruby/test/t/exception.rb
third-party/mruby/test/t/float.rb
third-party/mruby/test/t/hash.rb
third-party/mruby/test/t/integer.rb
third-party/mruby/test/t/kernel.rb
third-party/mruby/test/t/literals.rb
third-party/mruby/test/t/module.rb
third-party/mruby/test/t/numeric.rb
third-party/mruby/test/t/proc.rb
third-party/mruby/test/t/range.rb
third-party/mruby/test/t/string.rb
third-party/mruby/test/t/symbol.rb
third-party/mruby/test/t/syntax.rb
third-party/mruby/travis_config.rb
third-party/neverbleed/neverbleed.c
third-party/url-parser/url_parser.c [new file with mode: 0644]
third-party/url-parser/url_parser.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 85ac644..4da9664 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -14,6 +14,7 @@ github issues [2].
 --------
 
 187j3x1
+Adam Gołębiowski
 Alek Storm
 Alex Nalivko
 Alexandros Konstantinakis-Karmis
@@ -21,6 +22,7 @@ Alexis La Goutte
 Amir Pakdel
 Anders Bakken
 Andreas Pohl
+Andrew Penkrat
 Andy Davies
 Angus Gratton
 Anna Henningsen
@@ -28,6 +30,7 @@ Ant Bryan
 Benedikt Christoph Wolters
 Benjamin Peterson
 Bernard Spil
+Brendan Heinonen
 Brian Card
 Brian Suh
 Daniel Evers
@@ -36,6 +39,7 @@ Dave Reisner
 David Beitey
 David Weekly
 Dmitriy Vetutnev
+Don
 Dylan Plecki
 Etienne Cimon
 Fabian Möller
@@ -44,11 +48,14 @@ Gabi Davar
 Gitai
 Google Inc.
 Jacob Champion
+Jan Kundrát
 Jan-E
 Janusz Dziemidowicz
 Jay Satiro
+Jeff 'Raid' Baitis
 Jianqing Wang
 Jim Morrison
+Josh Braegger
 José F. Calcerrada
 Kamil Dudka
 Kazuho Oku
@@ -67,16 +74,19 @@ Mike Frysinger
 Mike Lothian
 Nicholas Hurley
 Nora Shoemaker
+Pedro Santos
 Peeyush Aggarwal
 Peter Wu
 Piotr Sikora
 Raul Gutierrez Segales
 Remo E
 Reza Tavakoli
+Richard Wolfert
 Rick Lei
 Ross Smith II
 Scott Mitchell
 Sebastiaan Deckers
+Simon Frankenberger
 Simone Basso
 Soham Sinha
 Stefan Eissing
@@ -96,6 +106,7 @@ Viacheslav Biriukov
 Viktor Szakats
 Viktor Szépe
 Wenfeng Liu
+William A Rowe Jr
 Xiaoguang Sun
 Zhuoyun Wei
 acesso
@@ -103,6 +114,7 @@ ayanamist
 bxshi
 clemahieu
 dalf
+dawg
 es
 fangdingjun
 jwchoi
index fccc1ce..741d449 100644 (file)
 
 cmake_minimum_required(VERSION 3.0)
 # XXX using 1.8.90 instead of 1.9.0-DEV
-project(nghttp2 VERSION 1.34.0)
+project(nghttp2 VERSION 1.40.0)
 
 # See versioning rule:
 #  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-set(LT_CURRENT  31)
-set(LT_REVISION 1)
-set(LT_AGE      17)
+set(LT_CURRENT  33)
+set(LT_REVISION 0)
+set(LT_AGE      19)
 
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 include(Version)
@@ -66,6 +66,7 @@ if(OPENSSL_FOUND AND LIBEV_FOUND AND ZLIB_FOUND)
 else()
   set(ENABLE_APP_DEFAULT OFF)
 endif()
+find_package(Systemd 209)
 find_package(Jansson  2.5)
 set(ENABLE_HPACK_TOOLS_DEFAULT ${JANSSON_FOUND})
 # 2.0.8 is required because we use evconnlistener_set_error_cb()
@@ -117,7 +118,7 @@ else()
 endif()
 
 include(ExtractValidFlags)
-foreach(_cxx1x_flag -std=c++11 -std=c++0x)
+foreach(_cxx1x_flag -std=c++14)
   extract_valid_cxx_flags(_cxx1x_flag_supported ${_cxx1x_flag})
   if(_cxx1x_flag_supported)
     set(CXX1XCXXFLAGS ${_cxx1x_flag})
@@ -193,6 +194,7 @@ endif()
 # libev (for src)
 set(HAVE_LIBEV      ${LIBEV_FOUND})
 set(HAVE_ZLIB       ${ZLIB_FOUND})
+set(HAVE_SYSTEMD    ${SYSTEMD_FOUND})
 set(HAVE_LIBEVENT_OPENSSL ${LIBEVENT_FOUND})
 if(LIBEVENT_FOUND)
   # Must both link the core and openssl libraries.
@@ -385,6 +387,15 @@ else()
   )
 endif()
 
+if(ENABLE_STATIC_CRT)
+  foreach(lang C CXX)
+    foreach(suffix "" _DEBUG _MINSIZEREL _RELEASE _RELWITHDEBINFO)
+      set(var "CMAKE_${lang}_FLAGS${suffix}")
+      string(REPLACE "/MD" "/MT" ${var} "${${var}}")
+    endforeach()
+  endforeach()
+endif()
+
 if(ENABLE_DEBUG)
   set(DEBUGBUILD 1)
 endif()
@@ -497,6 +508,7 @@ message(STATUS "summary of build options:
       Jansson:        ${HAVE_JANSSON} (LIBS='${JANSSON_LIBRARIES}')
       Jemalloc:       ${HAVE_JEMALLOC} (LIBS='${JEMALLOC_LIBRARIES}')
       Zlib:           ${HAVE_ZLIB} (LIBS='${ZLIB_LIBRARIES}')
+      Systemd:        ${HAVE_SYSTEMD} (LIBS='${SYSTEMD_LIBRARIES}')
       Boost::System:  ${Boost_SYSTEM_LIBRARY}
       Boost::Thread:  ${Boost_THREAD_LIBRARY}
     Third-party:
index a12479b..a8332bf 100644 (file)
@@ -15,6 +15,8 @@ option(ENABLE_PYTHON_BINDINGS "Build Python bindings"
 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(ENABLE_SHARED_LIB "Build libnghttp2 as a shared library" ON)
+option(ENABLE_STATIC_CRT "Build libnghttp2 against the MS LIBCMT[d]")
 
 option(WITH_LIBXML2     "Use libxml2"
   ${WITH_LIBXML2_DEFAULT})
index d973b9b..c17e088 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-commit 2b085815b787270b6942fc86a414edace12d40c5 (HEAD, tag: v1.34.0, origin/master, origin/HEAD, master)
+commit cc05c5fe8cfc8287ddc4930fa9684af878d9e631 (HEAD, tag: v1.40.0, origin/master, origin/HEAD, master)
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-10-04
+AuthorDate: 2019-11-15
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-10-04
+CommitDate: 2019-11-15
 
     Update manual pages
 
-commit 986fa3026479174c766417e19834adea6f70233c
+commit 66d7b194d4e27c8a316cbc5e8dca1722394401a9
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-10-04
+AuthorDate: 2019-11-15
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-10-04
+CommitDate: 2019-11-15
 
-    Bump up version number to 1.34.0, LT revision to 31:1:17
+    Update AUTHORS
 
-commit 7c8cb3a0ce08b3a2244e339654cca98033328acc
+commit 41060943bdeff0ed575e3f0c011a0df17b9d8027
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-10-04
+AuthorDate: 2019-11-15
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-10-04
+CommitDate: 2019-11-15
 
-    nghttpx: Improve CONNECT response status handling
+    Bump up version number to 1.40.0, LT revision to 33:0:19
 
-commit 334c439ce05b721963be56341348c1d58289051a
+commit 5ae9bb8925c1cb1e945b41d140367f27b3dd5c98
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-10-04
+AuthorDate: 2019-11-09
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-10-04
+CommitDate: 2019-11-09
 
-    Fix bug that regular CONNECT does not work
+    Fail fast if huffman decoding context is in failure state
 
-commit 6700626c304d435c06c2aa8a65ef8ca09076ad9f
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-10-03
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-10-03
-
-    Rule out content-length in the successful response to CONNECT
-
-commit 15162addc4bd6c55798300c007647a43ab197683
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-10-02
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-10-02
-
-    Update manual pages
-
-commit 9327077741eab5271d29233bc80d6090348264fb
-Merge: fc7489e0 aeb92bbb
+commit bb519154fe62f7ff7e5eb7269e05043dec6d3682
+Merge: db9a8f6e 77f5487a
 Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
-AuthorDate: 2018-09-30
+AuthorDate: 2019-11-02
 Commit:     GitHub <noreply@github.com>
-CommitDate: 2018-09-30
+CommitDate: 2019-11-02
 
-    Merge pull request #1235 from nghttp2/backend-conn-timeout
+    Merge pull request #1413 from nghttp2/check-authority
     
-    nghttpx: Add read/write-timeout parameters to backend option
+    Add nghttp2_check_authority as public API
 
-commit aeb92bbbe29a552d2c68236c64a8176a0dcdbd8b
+commit 77f5487a588f3c02a9671b7dd684186a5c34c1ea
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-30
+AuthorDate: 2019-11-02
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-30
+CommitDate: 2019-11-02
 
-    nghttpx: Add read/write-timeout parameters to backend option
+    Add nghttp2_check_authority as public API
 
-commit fc7489e044720818c041fb05dcde35c4abd2108b
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-30
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-30
-
-    nghttpx: Fix mruby parameter validation
-
-commit 87ac872fdcf828d7205355435c671f3cfb53fcdd
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-30
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-30
-
-    nghttpx: Update doc
-
-commit c278adde7a2ae9871b10a0d572edaa5be29b20dc
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-30
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-30
+commit db9a8f6efe6cc692e09c81fed200d159071566d1
+Merge: 6f28a69b 6ce4835e
+Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
+AuthorDate: 2019-10-29
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2019-10-29
 
-    nghttpx: Log error when mruby file cannot be opened
+    Merge pull request #1409 from nghttp2/fix-wrong-stream-close-error-code
+    
+    Fix the bug that stream is closed with wrong error code
 
-commit f94d72090996e0971c1b7bd3f1fbc367c1cb32e4
-Merge: 9b9baa6b d2a594a7
+commit 6f28a69b7d5bd93b67e2ca7af04bf9fa0b3c5c89
+Merge: d08c4395 29042f1c
 Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
-AuthorDate: 2018-09-29
+AuthorDate: 2019-10-29
 Commit:     GitHub <noreply@github.com>
-CommitDate: 2018-09-29
+CommitDate: 2019-10-29
 
-    Merge pull request #1234 from nghttp2/nghttpx-rfc8441
+    Merge pull request #1411 from richard78917/fix_warning
     
-    nghttpx: Implement RFC 8441 Bootstrapping WebSocket with HTTP/2
+    priority_spec::valid(): remove const qualifier from return value
 
-commit 9b9baa6bd9f4fd4a98c3747d5b17e5976c635869
+commit 6ce4835eeafe5bcea91c9672fcd3f9ef68ebd472
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-29
+AuthorDate: 2019-10-29
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-29
+CommitDate: 2019-10-29
 
-    Update doc
+    Fix the bug that stream is closed with wrong error code
+    
+    This commit fixes the bug that stream is closed with wrong error code
+    (0).  This happens when STREAM or DATA frame with END_STREAM flag set
+    is received and it violates HTTP messaging rule (i.e., content-length
+    does not match) and the other side of stream has been closed.  In this
+    case, nghttp2_on_stream_close_callback should be called with nonzero
+    error code, but previously it is called with 0 (NO_ERROR).
+
+commit 29042f1c95ef5d17fde7e8d972d2af24e47adf1b
+Author:     Richard Wolfert <richard.wolfert@mavenir.com>
+AuthorDate: 2019-10-29
+Commit:     Richard Wolfert <richard.wolfert@mavenir.com>
+CommitDate: 2019-10-29
+
+    priority_spec::valid(): remove const qualifier from return value
+    
+    gcc generates warning:
+    * type qualifiers ignored on function return type [-Wignored-qualifiers]
 
-commit 02566ee383fee0ccd628e6199519f0f2203a02e8
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-29
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-29
+commit d08c43951f5bf599dace882ed0e67ded2ff8519e
+Merge: 6f967c6e 5d6964cf
+Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
+AuthorDate: 2019-10-12
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2019-10-12
 
-    nghttpx: Update doc
+    Merge pull request #1405 from nghttp2/huffman
+    
+    Faster Huffman encoding/decoding
 
-commit 3002f31b1fafc7d07cc3dd8d34b2ccb6f7768a0d
+commit 5d6964cf81aaf41b4af5fdfbf4b06737bcb8f9a0
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-29
+AuthorDate: 2019-10-08
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-29
+CommitDate: 2019-10-12
 
-    src: Add debug output for SETTINGS_ENABLE_CONNECT_PROTOCOL
+    Faster huffman decoding
 
-commit d2a594a75340a02c312b70199a2532956968c594
+commit 0d855bfc1ba6e4201e21d7d130dbaad0907c8a3e
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-03-11
+AuthorDate: 2019-10-08
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-29
+CommitDate: 2019-10-12
 
-    nghttpx: Implement RFC 8441 Bootstrapping WebSocket with HTTP/2
+    Faster huffman encoding
 
-commit 651e14771182e2292833d84ab513e5f018df51c2
+commit 6f967c6ef3ea5799b59a5cea6b6f4bec10d25b04
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-28
+AuthorDate: 2019-09-21
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-28
+CommitDate: 2019-09-21
 
-    Allow client sending :protocol optimistically
+    Fix errors reported by coverity scan
 
-commit a42faf1cc21ca64218fc468442e02d439249c25f
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-23
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-23
+commit b8a43db84c5ce9a91cede2f4faf4706c1b3f4010
+Merge: 70b62c1a 28b1f0b9
+Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
+AuthorDate: 2019-09-21
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2019-09-21
 
-    nghttpx: Write TLS alert during handshake
+    Merge pull request #1394 from wrowe/fix-static-libname
+    
+    Avoid filename collision of static and dynamic lib
 
-commit 4aac05e1936ad14d66b994b789ab0abe0354a28c
-Merge: 8753b6da b80dfaa8
+commit 70b62c1a32eb56edc67eae0609949e0830ec14ad
+Merge: 1dd966f1 72b71a6b
 Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
-AuthorDate: 2018-09-23
+AuthorDate: 2019-09-21
 Commit:     GitHub <noreply@github.com>
-CommitDate: 2018-09-23
+CommitDate: 2019-09-21
 
-    Merge pull request #1231 from nghttp2/ws-lib-only
+    Merge pull request #1393 from wrowe/fix-static-msvcrt
     
-    Implement RFC 8441
+    Add new flag ENABLE_STATIC_CRT for Windows
 
-commit b80dfaa8a0e61b375b5f0f878ee1a56b7a3fbd64
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-23
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-23
+commit 28b1f0b90f82cccb3f1977ed3b580d2c98421f94
+Author:     William A Rowe Jr <wrowe@pivotal.io>
+AuthorDate: 2019-09-16
+Commit:     William A Rowe Jr <wrowe@pivotal.io>
+CommitDate: 2019-09-16
 
-    Adjustment for RFC 8441
+    Avoid filename collision of static and dynamic lib
+    
+    Renames the output of the ENABLE_STATIC_LIB library/archive output
+    to nghttp2_static.lib/.a to avoid filenames colliding with the output
+    name for ENABLE_SHARED_LIB library/archive, when both are enabled.
+    
+    Signed-off-by: William A Rowe Jr <wrowe@pivotal.io>
+    Signed-off-by: Yechiel Kalmenson <ykalmenson@pivotal.io>
 
-commit a19d8f5d31047944a29a6b865fd7fca07a71eba6
+commit 1dd966f1897421fe10d1ad91cfe466526636fcad
+Merge: f8933fe5 fe8946dd
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-03-11
+AuthorDate: 2019-09-17
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-23
+CommitDate: 2019-09-17
 
-    Deal with :protocol pseudo header
+    Merge branch 'fix-nghttpx-mruby'
 
-commit 33f6e90a56da48d9af9e635bed39510f2e2705ff
+commit fe8946ddc7c081eb0b2f376fc99279121a3f2a8b
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-03-10
+AuthorDate: 2019-09-16
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-23
+CommitDate: 2019-09-16
 
-    Add NGHTTP2_TOKEN__PROTOCOL
+    nghttpx: Fix bug that mruby is incorrectly shared between backends
+    
+    Previously, mruby context is wrongly shared by multiple patterns if
+    the underlying SharedDownstreamAddr is shared by multiple
+    DownstreamAddrGroups.  This commit fixes it.
 
-commit ed7fabcbc24b376bbf4b168aebcdb6f1a32cf688
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-03-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-23
+commit 72b71a6ba3f843bdea27f5c1cf926a5e1b152e48
+Author:     William A Rowe Jr <wrowe@pivotal.io>
+AuthorDate: 2019-09-14
+Commit:     William A Rowe Jr <wrowe@pivotal.io>
+CommitDate: 2019-09-14
 
-    Add SETTINGS_ENABLE_CONNECT_PROTOCOL
+    Add new flag ENABLE_STATIC_CRT for Windows
+    
+    This change adds the CMake option;
+    
+      ENABLE_STATIC_CRT  Build libnghttp2 against the MS LIBCMT[d]
+    
+    This avoids linking to msvcrt.lib for binaries to compile (/MT[d])
+    and link against the static C Runtime libcrt.lib, and
+    avoiding the msvcrt[d].dll dependency.
+    
+    Signed-off-by: William A Rowe Jr <wrowe@pivotal.io>
+    Signed-off-by: Yechiel Kalmenson <ykalmenson@pivotal.io>
 
-commit 8753b6da1419a267e10b1a47a37825e0b748128a
+commit f8933fe50468413eb149df7e331e7335400f1649
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-17
+AuthorDate: 2019-09-07
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-17
+CommitDate: 2019-09-07
 
-    Update doc
+    nghttpx: Reconnect h1 backend if it lost connection before sending headers
+    
+    This is the second attempt.  The first attempt was
+    8a59ce6d37471ec7a437d4700cabd98c55115b1e and it failed.
 
-commit f2de733bdf47c5dc16169fe45bbca870234efb98
+commit 89c33d690fd4667dd07c5791638eb6a4653a7f73
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-16
+AuthorDate: 2019-09-07
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-16
+CommitDate: 2019-09-07
 
-    Update neverbleed to fix OpenSSL 1.1.1 issues
+    Update neverbleed
 
-commit 88ff8c69a0621b335794271ade7003aad65bd5ef
+commit 7079dc5e753c1881aa6f162bbb4a9bb6d44e79ed
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-16
+AuthorDate: 2019-09-06
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-16
+CommitDate: 2019-09-06
 
-    Update mruby 1.4.1
+    Update neverbleed to fix memory leak
 
-commit a63558a1ebe3d50f4b8ab339e0d3ecdc858803c3
+commit 5080db84e267d9b036c08ac7b2de8b7696c4d6b2
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-16
+AuthorDate: 2019-09-06
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-16
+CommitDate: 2019-09-06
 
-    nghttpx: Call OCSP_response_get1_basic only when OCSP status is successful
+    Revert "nghttpx: Reconnect h1 backend if it lost connection before sending headers"
+    
+    This reverts commit 8a59ce6d37471ec7a437d4700cabd98c55115b1e.
 
-commit 3575a1325e8664bf7a55c5e4c590a9a0f9d27b79
+commit 053c7ac5889c280e1108b2bdd40c48d40920172a
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-15
+AuthorDate: 2019-09-03
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-15
+CommitDate: 2019-09-03
 
-    nghttpx: Fix crash with plain text HTTP
+    nghttpx: Returns 408 if backend timed out before sending headers
 
-commit e2de2fee69b691013e444a23254ff8cd13d01d30
+commit 8a59ce6d37471ec7a437d4700cabd98c55115b1e
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-15
+AuthorDate: 2019-09-03
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-15
+CommitDate: 2019-09-03
 
-    Update bash_completion
+    nghttpx: Reconnect h1 backend if it lost connection before sending headers
 
-commit 9f415979fbd0f2a9257ec1d86ddfe849ebad8c50
+commit f2fde180cdcca1f0a1c12d097c92510a887f6500
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-15
+AuthorDate: 2019-08-19
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-15
-
-    Update manual pages
-
-commit 4bfc0cd196ae6ca459b847698f6756ba732c79aa
-Merge: a1ea1696 9c824b87
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-14
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2018-09-14
+CommitDate: 2019-08-19
 
-    Merge pull request #1230 from nghttp2/nghttpx-faster-logging
+    Remove redundant null check before delete
     
-    nghttpx: Get rid of std::stringstream from Log
+    Reported in https://github.com/nghttp2/nghttp2/issues/1384
 
-commit 9c824b87fe647c4d587fe365d9bd63704ec826fe
+commit 95efb3e19d174354ca50c65d5d7227d92bcd60e1
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-08-31
+AuthorDate: 2019-06-25
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-14
+CommitDate: 2019-08-14
 
-    nghttpx: Get rid of std::stringstream from Log
+    Don't read too greedily
 
-commit a1ea1696becea1d3c3c2720d44acbf5dae759560
+commit 0a6ce87c22c69438ecbffe52a2859c3a32f1620f
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-13
+AuthorDate: 2019-06-25
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-13
+CommitDate: 2019-08-14
 
-    Make VALID_HD_NAME_CHARS and VALID_HD_VALUE_CHARS const qualified
+    Add nghttp2_option_set_max_outbound_ack
 
-commit dfc0f248c60140194626c526221e45b6ee0b3d45
+commit 2aa79fa91d2714ad704277c1c027c7f18cdd94f0
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-13
+AuthorDate: 2019-08-14
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-13
+CommitDate: 2019-08-14
 
-    Make static_table const qualified
+    Bump up LT revision to 32:0:18
 
-commit ed7c9db2a657005fa80eb44593f99dbbf57e6dec
+commit 3980678d24ff1bd84b95ec88d1acc07326af3e62
+Merge: 448bbbc3 319d5ab1
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-09
+AuthorDate: 2019-08-06
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-09
+CommitDate: 2019-08-06
 
-    nghttpx: Add mruby env.tls_handshake_finished
+    Merge branch 'nghttpx-fix-request-stall'
 
-commit 5b42815afb6214ff22e9228ade7021b5c0bf26c4
+commit 319d5ab1c6d916b6b8a0d85b2ae3f01b3ad04f2c
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-09
+AuthorDate: 2019-08-06
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-09
+CommitDate: 2019-08-06
 
-    nghttpx: Strip incoming Early-Data header field by default
-
-commit cfe7fa9a754c93e807fe2bc9e9046e3872969fa9
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-09
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-09
-
-    nghttpx: Add --tls13-ciphers and --tls-client-ciphers options
-
-commit cb8a9d58fdb3bff492d27977d5d5616220150ac0
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-09
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-09
-
-    src: Remove TLSv1.3 ciphers from DEFAULT_CIPHER_LIST
+    nghttpx: Fix request stall
     
-    TLSv1.3 ciphers are treated differently from the ciphers for TLSv1.2
-    or earlier.
+    Fix request stall if backend connection is reused and buffer is full.
 
-commit 023b94480bc365afbd3b2740323f3f02787b2901
-Merge: f79a5812 9b03c64f
+commit 448bbbc38c0f260447d0aae579d678885aa0f16c
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-09
+AuthorDate: 2019-08-06
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-09
+CommitDate: 2019-08-06
 
-    Merge branch 'tls13-early-data'
+    integration-tests: gofmt
 
-commit 9b03c64f68077fb54a68b4cae9fe35ca1d0a00ed
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-08
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
-
-    nghttpx: Should postpone early data by default
-
-commit b8eccec62dc7662d2c69aa129cfe5423c36ade5b
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-08
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
-
-    nghttpx: Disable OpenSSL anti-replay
-
-commit 9f212587205aebd7dbaabbe3472527140d377d20
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-05-20
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
-
-    Specify SSL_CTX_set_max_early_data and add an option to change max value
-
-commit 47f6012407fda0a49e467344bfe35b73279c9654
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-11-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
-
-    nghttpx: Add an option to postpone early data processing
-
-commit 770e44de4d31bfa3c3989306d087f14ce2f765ad
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-11-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
+commit e575a2aad99fe57126ad817c57356ea0ce51d213
+Merge: 7a590893 4f7aedc9
+Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
+AuthorDate: 2019-08-01
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2019-08-01
 
-    Implement draft-ietf-httpbis-replay-02
+    Merge pull request #1377 from Aldrog/cmake_systemd
     
-    nghttpx sends early-data header field when forwarding requests which
-    are received in TLSv1.3 early data, and the TLS handshake is still in
-    progress.
+    Support building nghttpx with systemd using cmake
 
-commit 2ab319c1375bd3a8a7d07faffa28fc4e9da00041
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-11-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
+commit 4f7aedc9d274183f4db17634d320a6d60ef2edb5
+Author:     Andrew Penkrat <Andrey.Penkrat@lanit-tercom.com>
+AuthorDate: 2019-07-29
+Commit:     Andrew Penkrat <Andrey.Penkrat@lanit-tercom.com>
+CommitDate: 2019-07-29
 
-    Don't hide error code from openssl
+    cmake: Support building nghttpx with systemd
 
-commit 3992302432acfb4f300bb2497cdd4355080a824c
+commit 7a5908933e55297ce2d8a0217391663ddf0c3f31
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-04-29
+AuthorDate: 2019-06-22
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
+CommitDate: 2019-06-22
 
-    Remove SSL_ERROR_WANT_WRITE handling
+    Fix clang-8 warning
 
-commit b30f312a70971a3fc570a86f963be0bfe0412232
+commit ee4431344511886efc66395a38b9bf5dddd7151b
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-04-01
+AuthorDate: 2019-06-11
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
+CommitDate: 2019-06-11
 
-    Honor SSL_read semantics
+    Fix FPE with default backend
 
-commit c5cdb78a952162aec9d0f2c9341742bb0b208631
+commit abef9b90ef2ba35ec25adbca7cb8d0b9a6c2d044
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-04-01
+AuthorDate: 2019-06-11
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-08
+CommitDate: 2019-06-11
 
-    nghttpx: Add TLSv1.3 0-RTT early data support
+    Fix log-level is not set with cmd-line or configuration file
 
-commit f79a58120e82e7e1382e60717b14d744daf803ab
+commit 12a999f0b8bc67706fdd5ff9b134ab67d495c76a
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-09-02
+AuthorDate: 2019-06-11
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-09-02
+CommitDate: 2019-06-11
 
-    Bump up version number to 1.34.0
+    Bump up version number to 1.40.0-DEV
index 4724a2a..a1ac0b0 100644 (file)
@@ -111,7 +111,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -299,7 +299,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index 0ed29e0..87f6ce4 100644 (file)
@@ -145,8 +145,10 @@ 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.
+Compiling libnghttp2 C source code requires a C99 compiler.  gcc 4.8
+is known to be adequate.  In order to compile the C++ source code, gcc
+>= 6.0 or clang >= 6.0 is required.  C++ source code requires C++14
+language features.
 
 .. note::
 
@@ -321,12 +323,7 @@ its testing framework.  We depend on the following libraries:
 * 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
+Go modules will download these dependencies automatically.
 
 To run the tests, run the following command under
 ``integration-tests`` directory:
@@ -1332,7 +1329,7 @@ are:
 * 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
+C++14 anonymous functions and closures.  The bare minimum example of
 an HTTP/2 server looks like this:
 
 .. code-block:: cpp
index 8ceb9ad..698d21c 100644 (file)
@@ -1652,7 +1652,7 @@ m4_include([m4/ax_boost_base.m4])
 m4_include([m4/ax_boost_system.m4])
 m4_include([m4/ax_boost_thread.m4])
 m4_include([m4/ax_check_compile_flag.m4])
-m4_include([m4/ax_cxx_compile_stdcxx_11.m4])
+m4_include([m4/ax_cxx_compile_stdcxx.m4])
 m4_include([m4/ax_python_devel.m4])
 m4_include([m4/libtool.m4])
 m4_include([m4/ltoptions.m4])
index 8f40d9f..e8a3cef 100644 (file)
@@ -40,6 +40,9 @@ if(LIBEVENT_INCLUDE_DIR)
     # Libevent 2.0
     file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h"
       LIBEVENT_VERSION REGEX "${_version_regex}")
+    if("${LIBEVENT_VERSION}" STREQUAL "")
+      set(LIBEVENT_VERSION ${PC_LIBEVENT_VERSION})
+    endif()
   else()
     # Libevent 1.4
     file(STRINGS "${LIBEVENT_INCLUDE_DIR}/event-config.h"
index 43fa5b1..eb3dbad 100644 (file)
@@ -30,8 +30,8 @@
 /* Define to 1 if your system has a working `chown' function. */
 #undef HAVE_CHOWN
 
-/* define if the compiler supports basic C++11 syntax */
-#undef HAVE_CXX11
+/* define if the compiler supports basic C++14 syntax */
+#undef HAVE_CXX14
 
 /* Define to 1 if you have the declaration of `initgroups', and to 0 if you
    don't. */
index b390905..696af86 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for nghttp2 1.34.0.
+# Generated by GNU Autoconf 2.69 for nghttp2 1.40.0.
 #
 # Report bugs to <t-tujikawa@users.sourceforge.net>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='nghttp2'
 PACKAGE_TARNAME='nghttp2'
-PACKAGE_VERSION='1.34.0'
-PACKAGE_STRING='nghttp2 1.34.0'
+PACKAGE_VERSION='1.40.0'
+PACKAGE_STRING='nghttp2 1.40.0'
 PACKAGE_BUGREPORT='t-tujikawa@users.sourceforge.net'
 PACKAGE_URL=''
 
@@ -693,7 +693,7 @@ CUNIT_CFLAGS
 ZLIB_LIBS
 ZLIB_CFLAGS
 CXX1XCXXFLAGS
-HAVE_CXX11
+HAVE_CXX14
 PYTHON_EXTRA_LDFLAGS
 PYTHON_EXTRA_LIBS
 PYTHON_SITE_PKG
@@ -1464,7 +1464,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures nghttp2 1.34.0 to adapt to many kinds of systems.
+\`configure' configures nghttp2 1.40.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1536,7 +1536,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of nghttp2 1.34.0:";;
+     short | recursive ) echo "Configuration of nghttp2 1.40.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1732,7 +1732,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-nghttp2 configure 1.34.0
+nghttp2 configure 1.40.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2692,7 +2692,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by nghttp2 $as_me 1.34.0, which was
+It was created by nghttp2 $as_me 1.40.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -6468,11 +6468,8 @@ _LT_EOF
   test $ac_status = 0; }; then
     # Now try to grab the symbols.
     nlist=conftest.nm
-    if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
-  (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; } && test -s "$nlist"; then
+    $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5
+    if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then
       # Try sorting and uniquifying the output.
       if sort "$nlist" | uniq > "$nlist"T; then
        mv -f "$nlist"T "$nlist"
@@ -8554,6 +8551,12 @@ lt_prog_compiler_static=
        lt_prog_compiler_pic='-KPIC'
        lt_prog_compiler_static='-static'
         ;;
+      # flang / f18. f95 an alias for gfortran or flang on Debian
+      flang* | f18* | f95*)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-fPIC'
+       lt_prog_compiler_static='-static'
+        ;;
       # icc used to be incompatible with GCC.
       # ICC 10 doesn't accept -KPIC any more.
       icc* | ifort*)
@@ -12660,7 +12663,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='nghttp2'
- VERSION='1.34.0'
+ VERSION='1.40.0'
 
 
 cat >>confdefs.h <<_ACEOF
 AM_BACKSLASH='\'
 
 
-LT_CURRENT=31
+LT_CURRENT=33
 
-LT_REVISION=1
+LT_REVISION=0
 
-LT_AGE=17
+LT_AGE=19
 
 
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/^0-9//g"`
 save_CXXFLAGS="$CXXFLAGS"
 CXXFLAGS=
 
-    ax_cxx_compile_cxx11_required=falsednl
+  ax_cxx_compile_alternatives="14 1y"    ax_cxx_compile_cxx14_required=false
   ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
 ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
   ac_success=no
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5
-$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; }
-if ${ax_cv_cxx_compile_cxx11+:} false; then :
+
+
+
+    if test x$ac_success = xno; then
+                for alternative in ${ax_cxx_compile_alternatives}; do
+      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+        cachevar=`$as_echo "ax_cv_cxx_compile_cxx14_$switch" | $as_tr_sh`
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++14 features with $switch" >&5
+$as_echo_n "checking whether $CXX supports C++14 features with $switch... " >&6; }
+if eval \${$cachevar+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+  ac_save_CXX="$CXX"
+           CXX="$CXX $switch"
+           cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-  template <typename T>
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+  namespace test_static_assert
+  {
+
+    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;
+  namespace test_final_override
+  {
 
-    typedef check<int> check_type;
-    check_type c;
-    check_type&& cr = static_cast<check_type&&>(c);
+    struct Base
+    {
+      virtual void f() {}
+    };
 
-    auto d = a;
+    struct Derived : public Base
+    {
+      virtual void f() override {}
+    };
 
-_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
-  ax_cv_cxx_compile_cxx11=yes
-else
-  ax_cv_cxx_compile_cxx11=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5
-$as_echo "$ax_cv_cxx_compile_cxx11" >&6; }
-  if test x$ax_cv_cxx_compile_cxx11 = xyes; then
-    ac_success=yes
-  fi
+  }
+
+  namespace test_double_right_angle_brackets
+  {
 
+    template < typename T >
+    struct check {};
 
+    typedef check<void> single_type;
+    typedef check<check<void>> double_type;
+    typedef check<check<check<void>>> triple_type;
+    typedef check<check<check<check<void>>>> quadruple_type;
 
-    if test x$ac_success = xno; then
-    for switch in -std=c++11 -std=c++0x; do
-      cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh`
-      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5
-$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; }
-if eval \${$cachevar+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_save_CXXFLAGS="$CXXFLAGS"
-         CXXFLAGS="$CXXFLAGS $switch"
-         cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
+  }
 
-  template <typename T>
-    struct check
+  namespace test_decltype
+  {
+
+    int
+    f()
     {
-      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+      int a = 1;
+      decltype(a) b = 2;
+      return a + b;
+    }
+
+  }
+
+  namespace test_type_deduction
+  {
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static const bool value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static const bool value = true;
     };
 
-    typedef check<check<bool>> right_angle_brackets;
+    template < typename T1, typename T2 >
+    auto
+    add(T1 a1, T2 a2) -> decltype(a1 + a2)
+    {
+      return a1 + a2;
+    }
 
-    int a;
-    decltype(a) b;
+    int
+    test(const int c, volatile int v)
+    {
+      static_assert(is_same<int, decltype(0)>::value == true, "");
+      static_assert(is_same<int, decltype(c)>::value == false, "");
+      static_assert(is_same<int, decltype(v)>::value == false, "");
+      auto ac = c;
+      auto av = v;
+      auto sumi = ac + av + 'x';
+      auto sumf = ac + av + 1.0;
+      static_assert(is_same<int, decltype(ac)>::value == true, "");
+      static_assert(is_same<int, decltype(av)>::value == true, "");
+      static_assert(is_same<int, decltype(sumi)>::value == true, "");
+      static_assert(is_same<int, decltype(sumf)>::value == false, "");
+      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+      return (sumf > 0.0) ? sumi : add(c, v);
+    }
+
+  }
+
+  namespace test_noexcept
+  {
+
+    int f() { return 0; }
+    int g() noexcept { return 0; }
+
+    static_assert(noexcept(f()) == false, "");
+    static_assert(noexcept(g()) == true, "");
+
+  }
+
+  namespace test_constexpr
+  {
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+    {
+      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+    }
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c(const CharT *const s) noexcept
+    {
+      return strlen_c_r(s, 0UL);
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("1") == 1UL, "");
+    static_assert(strlen_c("example") == 7UL, "");
+    static_assert(strlen_c("another\0example") == 7UL, "");
+
+  }
+
+  namespace test_rvalue_references
+  {
+
+    template < int N >
+    struct answer
+    {
+      static constexpr int value = N;
+    };
+
+    answer<1> f(int&)       { return answer<1>(); }
+    answer<2> f(const int&) { return answer<2>(); }
+    answer<3> f(int&&)      { return answer<3>(); }
+
+    void
+    test()
+    {
+      int i = 0;
+      const int c = 0;
+      static_assert(decltype(f(i))::value == 1, "");
+      static_assert(decltype(f(c))::value == 2, "");
+      static_assert(decltype(f(0))::value == 3, "");
+    }
+
+  }
+
+  namespace test_uniform_initialization
+  {
+
+    struct test
+    {
+      static const int zero {};
+      static const int one {1};
+    };
+
+    static_assert(test::zero == 0, "");
+    static_assert(test::one == 1, "");
+
+  }
+
+  namespace test_lambdas
+  {
+
+    void
+    test1()
+    {
+      auto lambda1 = [](){};
+      auto lambda2 = lambda1;
+      lambda1();
+      lambda2();
+    }
+
+    int
+    test2()
+    {
+      auto a = [](int i, int j){ return i + j; }(1, 2);
+      auto b = []() -> int { return '0'; }();
+      auto c = [=](){ return a + b; }();
+      auto d = [&](){ return c; }();
+      auto e = [a, &b](int x) mutable {
+        const auto identity = [](int y){ return y; };
+        for (auto i = 0; i < a; ++i)
+          a += b--;
+        return x + identity(a + b);
+      }(0);
+      return a + b + c + d + e;
+    }
+
+    int
+    test3()
+    {
+      const auto nullary = [](){ return 0; };
+      const auto unary = [](int x){ return x; };
+      using nullary_t = decltype(nullary);
+      using unary_t = decltype(unary);
+      const auto higher1st = [](nullary_t f){ return f(); };
+      const auto higher2nd = [unary](nullary_t f1){
+        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+      };
+      return higher1st(nullary) + higher2nd(nullary)(unary);
+    }
+
+  }
+
+  namespace test_variadic_templates
+  {
+
+    template <int...>
+    struct sum;
+
+    template <int N0, int... N1toN>
+    struct sum<N0, N1toN...>
+    {
+      static constexpr auto value = N0 + sum<N1toN...>::value;
+    };
+
+    template <>
+    struct sum<>
+    {
+      static constexpr auto value = 0;
+    };
+
+    static_assert(sum<>::value == 0, "");
+    static_assert(sum<1>::value == 1, "");
+    static_assert(sum<23>::value == 23, "");
+    static_assert(sum<1, 2>::value == 3, "");
+    static_assert(sum<5, 5, 11>::value == 21, "");
+    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+  }
+
+  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+  // because of this.
+  namespace test_template_alias_sfinae
+  {
+
+    struct foo {};
+
+    template<typename T>
+    using member = typename T::member_type;
+
+    template<typename T>
+    void func(...) {}
+
+    template<typename T>
+    void func(member<T>*) {}
+
+    void test();
+
+    void test() { func<foo>(0); }
+
+  }
+
+}  // namespace cxx11
+
+#endif  // __cplusplus >= 201103L
+
+
+
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+  namespace test_polymorphic_lambdas
+  {
+
+    int
+    test()
+    {
+      const auto lambda = [](auto&&... args){
+        const auto istiny = [](auto x){
+          return (sizeof(x) == 1UL) ? 1 : 0;
+        };
+        const int aretiny[] = { istiny(args)... };
+        return aretiny[0];
+      };
+      return lambda(1, 1L, 1.0f, '1');
+    }
+
+  }
+
+  namespace test_binary_literals
+  {
+
+    constexpr auto ivii = 0b0000000000101010;
+    static_assert(ivii == 42, "wrong value");
+
+  }
+
+  namespace test_generalized_constexpr
+  {
+
+    template < typename CharT >
+    constexpr unsigned long
+    strlen_c(const CharT *const s) noexcept
+    {
+      auto length = 0UL;
+      for (auto p = s; *p; ++p)
+        ++length;
+      return length;
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("x") == 1UL, "");
+    static_assert(strlen_c("test") == 4UL, "");
+    static_assert(strlen_c("another\0test") == 7UL, "");
+
+  }
+
+  namespace test_lambda_init_capture
+  {
+
+    int
+    test()
+    {
+      auto x = 0;
+      const auto lambda1 = [a = x](int b){ return a + b; };
+      const auto lambda2 = [a = lambda1(x)](){ return a; };
+      return lambda2();
+    }
+
+  }
+
+  namespace test_digit_separators
+  {
+
+    constexpr auto ten_million = 100'000'000;
+    static_assert(ten_million == 100000000, "");
+
+  }
+
+  namespace test_return_type_deduction
+  {
+
+    auto f(int& x) { return x; }
+    decltype(auto) g(int& x) { return x; }
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static constexpr auto value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static constexpr auto value = true;
+    };
+
+    int
+    test()
+    {
+      auto x = 0;
+      static_assert(is_same<int, decltype(f(x))>::value, "");
+      static_assert(is_same<int&, decltype(g(x))>::value, "");
+      return x;
+    }
+
+  }
+
+}  // namespace cxx14
+
+#endif  // __cplusplus >= 201402L
 
-    typedef check<int> check_type;
-    check_type c;
-    check_type&& cr = static_cast<check_type&&>(c);
 
-    auto d = a;
 
 _ACEOF
 if ac_fn_cxx_try_compile "$LINENO"; then :
@@ -18510,14 +18864,21 @@ else
   eval $cachevar=no
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-         CXXFLAGS="$ac_save_CXXFLAGS"
+           CXX="$ac_save_CXX"
 fi
 eval ac_res=\$$cachevar
               { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
 $as_echo "$ac_res" >&6; }
-      if eval test x\$$cachevar = xyes; then
-        CXXFLAGS="$CXXFLAGS $switch"
-        ac_success=yes
+        if eval test x\$$cachevar = xyes; then
+          CXX="$CXX $switch"
+          if test -n "$CXXCPP" ; then
+            CXXCPP="$CXXCPP $switch"
+          fi
+          ac_success=yes
+          break
+        fi
+      done
+      if test x$ac_success = xyes; then
         break
       fi
     done
@@ -18528,26 +18889,24 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
-  if test x$ax_cxx_compile_cxx11_required = xtrue; then
+  if test x$ax_cxx_compile_cxx14_required = xtrue; then
     if test x$ac_success = xno; then
-      as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5
+      as_fn_error $? "*** A compiler with support for C++14 language features is required." "$LINENO" 5
     fi
+  fi
+  if test x$ac_success = xno; then
+    HAVE_CXX14=0
+    { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++14 support was found" >&5
+$as_echo "$as_me: No compiler with C++14 support was found" >&6;}
   else
-    if test x$ac_success = xno; then
-      HAVE_CXX11=0
-      { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5
-$as_echo "$as_me: No compiler with C++11 support was found" >&6;}
-    else
-      HAVE_CXX11=1
-
-$as_echo "#define HAVE_CXX11 1" >>confdefs.h
-
-    fi
+    HAVE_CXX14=1
 
+$as_echo "#define HAVE_CXX14 1" >>confdefs.h
 
   fi
 
 
+
 CXX1XCXXFLAGS="$CXXFLAGS"
 CXXFLAGS="$save_CXXFLAGS"
 
@@ -24563,7 +24922,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by nghttp2 $as_me 1.34.0, which was
+This file was extended by nghttp2 $as_me 1.40.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -24629,7 +24988,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-nghttp2 config.status 1.34.0
+nghttp2 config.status 1.40.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -26655,9 +27014,9 @@ fi
       Failmalloc:     ${enable_failmalloc}
     Libs:
       OpenSSL:        ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
-      Libxml2:        ${have_libxml2} (CFLAGS='${LIBXML2_CPPFLAGS}' LIBS='${LIBXML2_LIBS}')
+      Libxml2:        ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}')
       Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
-      Libc-ares       ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_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}')
@@ -26719,9 +27078,9 @@ $as_echo "$as_me: summary of build options:
       Failmalloc:     ${enable_failmalloc}
     Libs:
       OpenSSL:        ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
-      Libxml2:        ${have_libxml2} (CFLAGS='${LIBXML2_CPPFLAGS}' LIBS='${LIBXML2_LIBS}')
+      Libxml2:        ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}')
       Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
-      Libc-ares       ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_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}')
index fbbdfcf..08a7bee 100644 (file)
@@ -25,7 +25,7 @@ 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], [t-tujikawa@users.sourceforge.net])
+AC_INIT([nghttp2], [1.40.0], [t-tujikawa@users.sourceforge.net])
 AC_CONFIG_AUX_DIR([.])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
@@ -44,9 +44,9 @@ 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, 1)
-AC_SUBST(LT_AGE, 17)
+AC_SUBST(LT_CURRENT, 33)
+AC_SUBST(LT_REVISION, 0)
+AC_SUBST(LT_AGE, 19)
 
 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"`
@@ -180,7 +180,7 @@ fi
 save_CXXFLAGS="$CXXFLAGS"
 CXXFLAGS=
 
-AX_CXX_COMPILE_STDCXX_11([noext], [optional])
+AX_CXX_COMPILE_STDCXX([14], [noext], [optional])
 
 CXX1XCXXFLAGS="$CXXFLAGS"
 CXXFLAGS="$save_CXXFLAGS"
@@ -899,9 +899,9 @@ AC_MSG_NOTICE([summary of build options:
       Failmalloc:     ${enable_failmalloc}
     Libs:
       OpenSSL:        ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
-      Libxml2:        ${have_libxml2} (CFLAGS='${LIBXML2_CPPFLAGS}' LIBS='${LIBXML2_LIBS}')
+      Libxml2:        ${have_libxml2} (CFLAGS='${LIBXML2_CFLAGS}' LIBS='${LIBXML2_LIBS}')
       Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
-      Libc-ares       ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_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}')
index f6cfe15..6c9da01 100644 (file)
@@ -118,7 +118,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -193,7 +193,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index 199c895..4d73cef 100644 (file)
@@ -27,6 +27,7 @@ APIDOCS= \
        macros.rst \
        enums.rst \
        types.rst \
+       nghttp2_check_authority.rst \
        nghttp2_check_header_name.rst \
        nghttp2_check_header_value.rst \
        nghttp2_hd_deflate_bound.rst \
@@ -67,6 +68,7 @@ APIDOCS= \
        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_option_set_max_outbound_ack.rst \
        nghttp2_pack_settings_payload.rst \
        nghttp2_priority_spec_check_default.rst \
        nghttp2_priority_spec_default_init.rst \
index 35d1132..0c30bf2 100644 (file)
@@ -118,7 +118,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -240,7 +240,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -384,6 +384,7 @@ APIDOCS = \
        macros.rst \
        enums.rst \
        types.rst \
+       nghttp2_check_authority.rst \
        nghttp2_check_header_name.rst \
        nghttp2_check_header_value.rst \
        nghttp2_hd_deflate_bound.rst \
@@ -424,6 +425,7 @@ APIDOCS = \
        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_option_set_max_outbound_ack.rst \
        nghttp2_pack_settings_payload.rst \
        nghttp2_priority_spec_check_default.rst \
        nghttp2_priority_spec_default_init.rst \
index c90776a..3022d45 100644 (file)
@@ -8,7 +8,7 @@ _h2load()
     _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" ) )
+            COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --log-file --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
index dede16f..d47e8b8 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "H2LOAD" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
+.TH "H2LOAD" "1" "Nov 15, 2019" "1.40.0" "nghttp2"
 .SH NAME
 h2load \- HTTP/2 benchmarking tool
 .
@@ -273,6 +273,17 @@ Default: \fB4K\fP
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-\-log\-file=<PATH>
+Write per\-request information to a file as tab\-separated
+columns: start  time as  microseconds since  epoch; HTTP
+status code;  microseconds until end of  response.  More
+columns may be added later.  Rows are ordered by end\-of\-
+response  time when  using  one worker  thread, but  may
+appear slightly  out of order with  multiple threads due
+to buffering.  Status code is \-1 for failed streams.
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-v, \-\-verbose
 Output debug information.
 .UNINDENT
@@ -377,13 +388,16 @@ range (mean +/\- sd) against total number of successful requests.
 .INDENT 7.0
 .TP
 .B min
-The minimum time taken to connect to a server.
+The minimum time taken to connect to a server including TLS
+handshake.
 .TP
 .B max
-The maximum time taken to connect to a server.
+The maximum time taken to connect to a server including TLS
+handshake.
 .TP
 .B mean
-The mean time taken to connect to a server.
+The mean time taken to connect to a server including TLS
+handshake.
 .TP
 .B sd
 The standard deviation of the time taken to connect to a server.
index f37a515..3edc9d9 100644 (file)
@@ -229,6 +229,16 @@ OPTIONS
 
     Default: ``4K``
 
+.. option:: --log-file=<PATH>
+
+    Write per-request information to a file as tab-separated
+    columns: start  time as  microseconds since  epoch; HTTP
+    status code;  microseconds until end of  response.  More
+    columns may be added later.  Rows are ordered by end-of-
+    response  time when  using  one worker  thread, but  may
+    appear slightly  out of order with  multiple threads due
+    to buffering.  Status code is -1 for failed streams.
+
 .. option:: -v, --verbose
 
     Output debug information.
@@ -313,11 +323,14 @@ time for request
 
 time for connect
   min
-    The minimum time taken to connect to a server.
+    The minimum time taken to connect to a server including TLS
+    handshake.
   max
-    The maximum time taken to connect to a server.
+    The maximum time taken to connect to a server including TLS
+    handshake.
   mean
-    The mean time taken to connect to a server.
+    The mean time taken to connect to a server including TLS
+    handshake.
   sd
     The standard deviation of the time taken to connect to a server.
   +/- sd
index 3cf1682..d34cf1e 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTP" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
+.TH "NGHTTP" "1" "Nov 15, 2019" "1.40.0" "nghttp2"
 .SH NAME
 nghttp \- HTTP/2 client
 .
diff --git a/doc/nghttp2_check_authority.rst b/doc/nghttp2_check_authority.rst
new file mode 100644 (file)
index 0000000..46aea3b
--- /dev/null
@@ -0,0 +1,19 @@
+
+nghttp2_check_authority
+=======================
+
+Synopsis
+--------
+
+*#include <nghttp2/nghttp2.h>*
+
+.. function:: int nghttp2_check_authority(const uint8_t *value, size_t len)
+
+    
+    Returns nonzero if the *value* which is supposed to the value of
+    :authority or host header field is valid according to
+    https://tools.ietf.org/html/rfc3986#section-3.2
+    
+    *value* is valid if it merely consists of the allowed characters.
+    In particular, it does not check whether *value* follows the syntax
+    of authority.
diff --git a/doc/nghttp2_option_set_max_outbound_ack.rst b/doc/nghttp2_option_set_max_outbound_ack.rst
new file mode 100644 (file)
index 0000000..5b24dcf
--- /dev/null
@@ -0,0 +1,16 @@
+
+nghttp2_option_set_max_outbound_ack
+===================================
+
+Synopsis
+--------
+
+*#include <nghttp2/nghttp2.h>*
+
+.. function:: void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val)
+
+    
+    This function sets the maximum number of outgoing SETTINGS ACK and
+    PING ACK frames retained in :type:`nghttp2_session` object.  If
+    more than those frames are retained, the peer is considered to be
+    misbehaving and session will be closed.  The default value is 1000.
index d3587f5..315d50b 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTPD" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
+.TH "NGHTTPD" "1" "Nov 15, 2019" "1.40.0" "nghttp2"
 .SH NAME
 nghttpd \- HTTP/2 server
 .
index 3d8bf31..9f18c70 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTPX" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
+.TH "NGHTTPX" "1" "Nov 15, 2019" "1.40.0" "nghttp2"
 .SH NAME
 nghttpx \- HTTP/2 proxy
 .
@@ -139,13 +139,13 @@ parameters       are:      "proto=<PROTO>",       "tls",
 "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
 "affinity=<METHOD>",    "dns",    "redirect\-if\-not\-tls",
 "upgrade\-scheme",                        "mruby=<PATH>",
-"read\-timeout=<DURATION>",                           and
-"write\-timeout=<DURATION>".   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.
+"read\-timeout=<DURATION>",   "write\-timeout=<DURATION>",
+"group=<GROUP>",  "group\-weight=<N>", and  "weight=<N>".
+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
@@ -251,6 +251,31 @@ timeouts.  If these timeouts  are entirely omitted for a
 pattern,            \fI\%\-\-backend\-read\-timeout\fP           and
 \fI\%\-\-backend\-write\-timeout\fP are used.
 .sp
+"group=<GROUP>"  parameter specifies  the name  of group
+this backend address belongs to.  By default, it belongs
+to  the unnamed  default group.   The name  of group  is
+unique   per   pattern.   "group\-weight=<N>"   parameter
+specifies the  weight of  the group.  The  higher weight
+gets  more frequently  selected  by  the load  balancing
+algorithm.  <N> must be  [1, 256] inclusive.  The weight
+8 has 4 times more weight  than 2.  <N> must be the same
+for  all addresses  which  share the  same <GROUP>.   If
+"group\-weight" is  omitted in an address,  but the other
+address  which  belongs  to  the  same  group  specifies
+"group\-weight",   its    weight   is   used.     If   no
+"group\-weight"  is  specified  for  all  addresses,  the
+weight of a group becomes 1.  "group" and "group\-weight"
+are ignored if session affinity is enabled.
+.sp
+"weight=<N>"  parameter  specifies  the  weight  of  the
+backend  address  inside  a  group  which  this  address
+belongs  to.  The  higher  weight  gets more  frequently
+selected by  the load balancing algorithm.   <N> must be
+[1,  256] inclusive.   The  weight 8  has  4 times  more
+weight  than weight  2.  If  this parameter  is omitted,
+weight  becomes  1.   "weight"  is  ignored  if  session
+affinity is enabled.
+.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.
index 5018df0..67ebff0 100644 (file)
@@ -123,13 +123,13 @@ Connections
     "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
     "affinity=<METHOD>",    "dns",    "redirect-if-not-tls",
     "upgrade-scheme",                        "mruby=<PATH>",
-    "read-timeout=<DURATION>",                           and
-    "write-timeout=<DURATION>".   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.
+    "read-timeout=<DURATION>",   "write-timeout=<DURATION>",
+    "group=<GROUP>",  "group-weight=<N>", and  "weight=<N>".
+    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
@@ -235,6 +235,31 @@ Connections
     pattern,            :option:`--backend-read-timeout`           and
     :option:`--backend-write-timeout` are used.
 
+    "group=<GROUP>"  parameter specifies  the name  of group
+    this backend address belongs to.  By default, it belongs
+    to  the unnamed  default group.   The name  of group  is
+    unique   per   pattern.   "group-weight=<N>"   parameter
+    specifies the  weight of  the group.  The  higher weight
+    gets  more frequently  selected  by  the load  balancing
+    algorithm.  <N> must be  [1, 256] inclusive.  The weight
+    8 has 4 times more weight  than 2.  <N> must be the same
+    for  all addresses  which  share the  same <GROUP>.   If
+    "group-weight" is  omitted in an address,  but the other
+    address  which  belongs  to  the  same  group  specifies
+    "group-weight",   its    weight   is   used.     If   no
+    "group-weight"  is  specified  for  all  addresses,  the
+    weight of a group becomes 1.  "group" and "group-weight"
+    are ignored if session affinity is enabled.
+
+    "weight=<N>"  parameter  specifies  the  weight  of  the
+    backend  address  inside  a  group  which  this  address
+    belongs  to.  The  higher  weight  gets more  frequently
+    selected by  the load balancing algorithm.   <N> must be
+    [1,  256] inclusive.   The  weight 8  has  4 times  more
+    weight  than weight  2.  If  this parameter  is omitted,
+    weight  becomes  1.   "weight"  is  ignored  if  session
+    affinity is enabled.
+
     Since ";" and ":" are  used as delimiter, <PATTERN> must
     not  contain these  characters.  Since  ";" has  special
     meaning in shell, the option value must be quoted.
index dc814a3..bea72b1 100644 (file)
@@ -26,16 +26,16 @@ 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.
+between versions, we currently use clang-format-8.
 
 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.
+your PATH or it exists under different name (e.g., clang-format-diff-8
+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
index 40c74e6..11d3eb0 100644 (file)
@@ -498,6 +498,14 @@ naming scheme and structure, nghttpx provides the new option
 :option:`--tls13-ciphers` and :option:`--tls13-client-ciphers` to
 change preferred cipher list for TLSv1.3.
 
+WebSockets over HTTP/2
+----------------------
+
+nghttpx supports `RFC 8441 <https://tools.ietf.org/html/rfc8441>`_
+Bootstrapping WebSockets with HTTP/2 for both frontend and backend
+connections.  This feature is enabled by default and no configuration
+is required.
+
 Migration from nghttpx v1.18.x or earlier
 -----------------------------------------
 
index 9795ad7..7f700b7 100644 (file)
@@ -9,6 +9,7 @@ if(ENABLE_EXAMPLES)
   include_directories(
     ${CMAKE_CURRENT_SOURCE_DIR}
     "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
+    "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/llhttp/include"
 
     ${LIBEVENT_INCLUDE_DIRS}
     ${OPENSSL_INCLUDE_DIRS}
@@ -21,14 +22,24 @@ if(ENABLE_EXAMPLES)
     ${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>)
+  add_executable(client           client.c $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
+  add_executable(libevent-client  libevent-client.c $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
+  add_executable(libevent-server  libevent-server.c $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
+  add_executable(deflate          deflate.c $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
 
   if(ENABLE_ASIO_LIB)
     foreach(name asio-sv asio-sv2 asio-cl asio-cl2)
-      add_executable(${name} ${name}.cc $<TARGET_OBJECTS:http-parser>)
+      add_executable(${name} ${name}.cc $<TARGET_OBJECTS:llhttp>
+        $<TARGET_OBJECTS:url-parser>
+      )
       target_include_directories(${name} PRIVATE
         ${OPENSSL_INCLUDE_DIRS}
         ${Boost_INCLUDE_DIRS}
index 2638a45..b22279d 100644 (file)
@@ -36,7 +36,7 @@ AM_CPPFLAGS = \
        @OPENSSL_CFLAGS@ \
        @DEFS@
 LDADD = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la \
+       $(top_builddir)/third-party/liburl-parser.la \
        @LIBEVENT_OPENSSL_LIBS@ \
        @OPENSSL_LIBS@ \
        @APPLDFLAGS@
@@ -61,7 +61,7 @@ noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
 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 \
+       $(top_builddir)/third-party/liburl-parser.la \
        @OPENSSL_LIBS@ \
        ${BOOST_LDFLAGS} \
        ${BOOST_ASIO_LIB} \
index eb5d825..a47209d 100644 (file)
@@ -124,7 +124,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -148,7 +148,7 @@ asio_cl_OBJECTS = $(am_asio_cl_OBJECTS)
 am__DEPENDENCIES_1 =
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(top_builddir)/src/libnghttp2_asio.la \
-@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(top_builddir)/third-party/libhttp-parser.la \
+@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(top_builddir)/third-party/liburl-parser.la \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(am__DEPENDENCIES_1) \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(am__DEPENDENCIES_1) \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(am__DEPENDENCIES_1) \
@@ -176,14 +176,14 @@ client_OBJECTS = $(am_client_OBJECTS)
 client_LDADD = $(LDADD)
 @ENABLE_EXAMPLES_TRUE@client_DEPENDENCIES =  \
 @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \
-@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la
+@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburl-parser.la
 am__deflate_SOURCES_DIST = deflate.c
 @ENABLE_EXAMPLES_TRUE@am_deflate_OBJECTS = deflate.$(OBJEXT)
 deflate_OBJECTS = $(am_deflate_OBJECTS)
 deflate_LDADD = $(LDADD)
 @ENABLE_EXAMPLES_TRUE@deflate_DEPENDENCIES =  \
 @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \
-@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la
+@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburl-parser.la
 am__libevent_client_SOURCES_DIST = libevent-client.c
 @ENABLE_EXAMPLES_TRUE@am_libevent_client_OBJECTS =  \
 @ENABLE_EXAMPLES_TRUE@ libevent-client.$(OBJEXT)
@@ -191,7 +191,7 @@ libevent_client_OBJECTS = $(am_libevent_client_OBJECTS)
 libevent_client_LDADD = $(LDADD)
 @ENABLE_EXAMPLES_TRUE@libevent_client_DEPENDENCIES =  \
 @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \
-@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la
+@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburl-parser.la
 am__libevent_server_SOURCES_DIST = libevent-server.c
 @ENABLE_EXAMPLES_TRUE@am_libevent_server_OBJECTS =  \
 @ENABLE_EXAMPLES_TRUE@ libevent-server.$(OBJEXT)
@@ -199,7 +199,7 @@ libevent_server_OBJECTS = $(am_libevent_server_OBJECTS)
 libevent_server_LDADD = $(LDADD)
 @ENABLE_EXAMPLES_TRUE@libevent_server_DEPENDENCIES =  \
 @ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \
-@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la
+@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburl-parser.la
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -334,7 +334,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -486,7 +486,7 @@ EXTRA_DIST = CMakeLists.txt
 @ENABLE_EXAMPLES_TRUE@ @DEFS@
 
 @ENABLE_EXAMPLES_TRUE@LDADD = $(top_builddir)/lib/libnghttp2.la \
-@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \
+@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/liburl-parser.la \
 @ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_LIBS@ \
 @ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ \
 @ENABLE_EXAMPLES_TRUE@ @APPLDFLAGS@
@@ -502,7 +502,7 @@ EXTRA_DIST = CMakeLists.txt
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
-@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(top_builddir)/third-party/libhttp-parser.la \
+@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   $(top_builddir)/third-party/liburl-parser.la \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   @OPENSSL_LIBS@ \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   ${BOOST_LDFLAGS} \
 @ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@   ${BOOST_ASIO_LIB} \
index 22b46da..f42cbdb 100644 (file)
@@ -65,7 +65,7 @@ char *strndup(const char *s, size_t size);
 
 #include <nghttp2/nghttp2.h>
 
-#include "http-parser/http_parser.h"
+#include "url-parser/url_parser.h"
 
 #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
 
index 1c5fe9d..0bd0337 100644 (file)
@@ -39,11 +39,6 @@ EXTRA_DIST = \
        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 21e741d..539599d 100644 (file)
@@ -118,7 +118,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -194,7 +194,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -550,11 +550,6 @@ uninstall-am:
 .PRECIOUS: Makefile
 
 
-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 a765333..3d41677 100644 (file)
@@ -625,6 +625,35 @@ func TestH1H1HTTPSRedirectPort(t *testing.T) {
        }
 }
 
+// TestH1H1POSTRequests tests that server can handle 2 requests with
+// request body.
+func TestH1H1POSTRequests(t *testing.T) {
+       st := newServerTester(nil, t, noopHandler)
+       defer st.Close()
+
+       res, err := st.http1(requestParam{
+               name: "TestH1H1POSTRequestsNo1",
+               body: make([]byte, 1),
+       })
+       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)
+       }
+
+       res, err = st.http1(requestParam{
+               name: "TestH1H1POSTRequestsNo2",
+               body: make([]byte, 65536),
+       })
+       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)
+       }
+}
+
 // // TestH1H2ConnectFailure tests that server handles the situation that
 // // connection attempt to HTTP/2 backend failed.
 // func TestH1H2ConnectFailure(t *testing.T) {
index d463131..89e7f29 100644 (file)
@@ -627,15 +627,15 @@ func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr
 }
 
 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
+       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
@@ -662,7 +662,9 @@ func cloneHeader(h http.Header) http.Header {
        return h2
 }
 
-func noopHandler(w http.ResponseWriter, r *http.Request) {}
+func noopHandler(w http.ResponseWriter, r *http.Request) {
+       ioutil.ReadAll(r.Body)
+}
 
 type APIResponse struct {
        Status string                 `json:"status,omitempty"`
index 17e422b..4e3f5da 100644 (file)
@@ -38,16 +38,23 @@ if(WIN32)
 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
+if(ENABLE_SHARED_LIB)
+  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"
-    )
+  )
+
+  install(TARGETS nghttp2
+    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif()
 
 if(HAVE_CUNIT OR ENABLE_STATIC_LIB)
   # Static library (for unittests because of symbol visibility)
@@ -55,7 +62,7 @@ if(HAVE_CUNIT OR ENABLE_STATIC_LIB)
   set_target_properties(nghttp2_static PROPERTIES
     COMPILE_FLAGS "${WARNCFLAGS}"
     VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
-    ARCHIVE_OUTPUT_NAME nghttp2
+    ARCHIVE_OUTPUT_NAME nghttp2_static
   )
   target_compile_definitions(nghttp2_static PUBLIC "-DNGHTTP2_STATICLIB")
   if(ENABLE_STATIC_LIB)
@@ -64,8 +71,6 @@ if(HAVE_CUNIT OR ENABLE_STATIC_LIB)
   endif()
 endif()
 
-install(TARGETS nghttp2
-  DESTINATION "${CMAKE_INSTALL_LIBDIR}")
 
 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2.pc"
   DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
index 7a8bc30..d94da88 100644 (file)
@@ -112,7 +112,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -333,7 +333,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index f30d6c5..f12b8f6 100644 (file)
@@ -119,7 +119,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -242,7 +242,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index e7198b3..e3aeb9f 100644 (file)
 #  define WIN32
 #endif
 
+/* Compatibility for non-Clang compilers */
+#ifndef __has_declspec_attribute
+#  define __has_declspec_attribute(x) 0
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -51,7 +56,8 @@ extern "C" {
 
 #ifdef NGHTTP2_STATICLIB
 #  define NGHTTP2_EXTERN
-#elif defined(WIN32)
+#elif defined(WIN32) || (__has_declspec_attribute(dllexport) &&                \
+                         __has_declspec_attribute(dllimport))
 #  ifdef BUILDING_NGHTTP2
 #    define NGHTTP2_EXTERN __declspec(dllexport)
 #  else /* !BUILDING_NGHTTP2 */
@@ -2645,6 +2651,17 @@ NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
 /**
  * @function
  *
+ * This function sets the maximum number of outgoing SETTINGS ACK and
+ * PING ACK frames retained in :type:`nghttp2_session` object.  If
+ * more than those frames are retained, the peer is considered to be
+ * misbehaving and session will be closed.  The default value is 1000.
+ */
+NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option,
+                                                        size_t 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
@@ -4752,6 +4769,19 @@ NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len);
  */
 NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len);
 
+/**
+ * @function
+ *
+ * Returns nonzero if the |value| which is supposed to the value of
+ * :authority or host header field is valid according to
+ * https://tools.ietf.org/html/rfc3986#section-3.2
+ *
+ * |value| is valid if it merely consists of the allowed characters.
+ * In particular, it does not check whether |value| follows the syntax
+ * of authority.
+ */
+NGHTTP2_EXTERN int nghttp2_check_authority(const uint8_t *value, size_t len);
+
 /* HPACK API */
 
 struct nghttp2_hd_deflater;
index 420adbd..45d21e2 100644 (file)
@@ -29,7 +29,7 @@
  * @macro
  * Version number of the nghttp2 library release
  */
-#define NGHTTP2_VERSION "1.34.0"
+#define NGHTTP2_VERSION "1.40.0"
 
 /**
  * @macro
@@ -37,6 +37,6 @@
  * 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 0x012200
+#define NGHTTP2_VERSION_NUM 0x012800
 
 #endif /* NGHTTP2VER_H */
index a61f0d4..5e86931 100644 (file)
@@ -1390,7 +1390,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
   if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) {
     nghttp2_hd_nv hd_nv;
 
-    if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) {
+    if (idx != -1) {
       hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name;
       nghttp2_rcbuf_incref(hd_nv.name);
     } else {
@@ -1694,6 +1694,11 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater,
     DEBUGF("inflatehd: huffman decoding failed\n");
     return readlen;
   }
+  if (nghttp2_hd_huff_decode_failure_state(&inflater->huff_decode_ctx)) {
+    DEBUGF("inflatehd: huffman decoding failed\n");
+    return NGHTTP2_ERR_HEADER_COMP;
+  }
+
   inflater->left -= (size_t)readlen;
   return readlen;
 }
index 14ae980..2674028 100644 (file)
@@ -430,4 +430,10 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
                                nghttp2_buf *buf, const uint8_t *src,
                                size_t srclen, int fin);
 
+/*
+ * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx|
+ * indicates that huffman decoding context is in failure state.
+ */
+int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx);
+
 #endif /* NGHTTP2_HD_H */
index 8881aac..ac90f49 100644 (file)
 #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);
-}
+#include "nghttp2_net.h"
 
 size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) {
   size_t i;
@@ -151,81 +44,101 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) {
 
 int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
                            size_t srclen) {
-  int rv;
-  ssize_t rembits = 8;
-  size_t i;
+  const nghttp2_huff_sym *sym;
+  const uint8_t *end = src + srclen;
+  uint64_t code = 0;
+  uint32_t x;
+  size_t nbits = 0;
   size_t avail;
+  int rv;
 
   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);
+  for (; src != end;) {
+    sym = &huff_sym_table[*src++];
+    code |= (uint64_t)sym->code << (32 - nbits);
+    nbits += sym->nbits;
+    if (nbits < 32) {
+      continue;
+    }
+    if (avail >= 4) {
+      x = htonl((uint32_t)(code >> 32));
+      memcpy(bufs->cur->buf.last, &x, 4);
+      bufs->cur->buf.last += 4;
+      avail -= 4;
+      code <<= 32;
+      nbits -= 32;
+      continue;
+    }
+
+    for (; nbits >= 8;) {
+      rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56));
+      if (rv != 0) {
+        return rv;
       }
+      code <<= 8;
+      nbits -= 8;
     }
-    rembits = huff_encode_sym(bufs, &avail, (size_t)rembits, sym);
-    if (rembits < 0) {
-      return (int)rembits;
+
+    avail = nghttp2_bufs_cur_avail(bufs);
+  }
+
+  for (; nbits >= 8;) {
+    rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56));
+    if (rv != 0) {
+      return rv;
     }
+    code <<= 8;
+    nbits -= 8;
   }
-  /* 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)));
+
+  if (nbits) {
+    rv = nghttp2_bufs_addb(
+        bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1)));
+    if (rv != 0) {
+      return rv;
+    }
   }
 
   return 0;
 }
 
 void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
-  ctx->state = 0;
-  ctx->accept = 1;
+  ctx->fstate = NGHTTP2_HUFF_ACCEPTED;
 }
 
 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;
+  const uint8_t *end = src + srclen;
+  nghttp2_huff_decode node = {ctx->fstate, 0};
+  const nghttp2_huff_decode *t = &node;
+  uint8_t c;
 
   /* 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) {
+  for (; src != end;) {
+    c = *src++;
+    t = &huff_decode_table[t->fstate & 0x1ff][c >> 4];
+    if (t->fstate & 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) {
+    t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf];
+    if (t->fstate & NGHTTP2_HUFF_SYM) {
       *buf->last++ = t->sym;
     }
-
-    ctx->state = t->state;
-    ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
   }
-  if (final && !ctx->accept) {
+
+  ctx->fstate = t->fstate;
+
+  if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) {
     return NGHTTP2_ERR_HEADER_COMP;
   }
-  return (ssize_t)i;
+
+  return (ssize_t)srclen;
+}
+
+int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) {
+  return ctx->fstate == 0x100;
 }
index c6e3942..2bfd531 100644 (file)
 typedef enum {
   /* FSA accepts this state as the end of huffman encoding
      sequence. */
-  NGHTTP2_HUFF_ACCEPTED = 1,
+  NGHTTP2_HUFF_ACCEPTED = 1 << 14,
   /* This state emits symbol */
-  NGHTTP2_HUFF_SYM = (1 << 1),
-  /* If state machine reaches this state, decoding fails. */
-  NGHTTP2_HUFF_FAIL = (1 << 2)
+  NGHTTP2_HUFF_SYM = 1 << 15,
 } 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;
+  /* fstate is the current huffman decoding state, which is actually
+     the node ID of internal huffman tree with
+     nghttp2_huff_decode_flag OR-ed.  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.  The node ID 256 is
+     a special node and it is a terminal state that means decoding
+     failed. */
+  uint16_t fstate;
   /* symbol if NGHTTP2_HUFF_SYM flag set */
   uint8_t sym;
 } nghttp2_huff_decode;
@@ -56,12 +55,8 @@ typedef struct {
 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;
+  /* fstate is the current huffman decoding state. */
+  uint16_t fstate;
 } nghttp2_hd_huff_decode_context;
 
 typedef struct {
index 5ef4a95..2e2e13f 100644 (file)
 /* 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}};
+    {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u},
+    {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u},
+    {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u},
+    {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u},
+    {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u},
+    {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u},
+    {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u},
+    {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u},
+    {6, 0x50000000u},  {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u},
+    {13, 0xffc80000u}, {6, 0x54000000u},  {8, 0xf8000000u},  {11, 0xff400000u},
+    {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u},  {11, 0xff600000u},
+    {8, 0xfa000000u},  {6, 0x58000000u},  {6, 0x5c000000u},  {6, 0x60000000u},
+    {5, 0x0u},         {5, 0x8000000u},   {5, 0x10000000u},  {6, 0x64000000u},
+    {6, 0x68000000u},  {6, 0x6c000000u},  {6, 0x70000000u},  {6, 0x74000000u},
+    {6, 0x78000000u},  {6, 0x7c000000u},  {7, 0xb8000000u},  {8, 0xfb000000u},
+    {15, 0xfff80000u}, {6, 0x80000000u},  {12, 0xffb00000u}, {10, 0xff000000u},
+    {13, 0xffd00000u}, {6, 0x84000000u},  {7, 0xba000000u},  {7, 0xbc000000u},
+    {7, 0xbe000000u},  {7, 0xc0000000u},  {7, 0xc2000000u},  {7, 0xc4000000u},
+    {7, 0xc6000000u},  {7, 0xc8000000u},  {7, 0xca000000u},  {7, 0xcc000000u},
+    {7, 0xce000000u},  {7, 0xd0000000u},  {7, 0xd2000000u},  {7, 0xd4000000u},
+    {7, 0xd6000000u},  {7, 0xd8000000u},  {7, 0xda000000u},  {7, 0xdc000000u},
+    {7, 0xde000000u},  {7, 0xe0000000u},  {7, 0xe2000000u},  {7, 0xe4000000u},
+    {8, 0xfc000000u},  {7, 0xe6000000u},  {8, 0xfd000000u},  {13, 0xffd80000u},
+    {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u},
+    {15, 0xfffa0000u}, {5, 0x18000000u},  {6, 0x8c000000u},  {5, 0x20000000u},
+    {6, 0x90000000u},  {5, 0x28000000u},  {6, 0x94000000u},  {6, 0x98000000u},
+    {6, 0x9c000000u},  {5, 0x30000000u},  {7, 0xe8000000u},  {7, 0xea000000u},
+    {6, 0xa0000000u},  {6, 0xa4000000u},  {6, 0xa8000000u},  {5, 0x38000000u},
+    {6, 0xac000000u},  {7, 0xec000000u},  {6, 0xb0000000u},  {5, 0x40000000u},
+    {5, 0x48000000u},  {6, 0xb4000000u},  {7, 0xee000000u},  {7, 0xf0000000u},
+    {7, 0xf2000000u},  {7, 0xf4000000u},  {7, 0xf6000000u},  {15, 0xfffc0000u},
+    {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u},
+    {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u},
+    {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u},
+    {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u},
+    {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u},
+    {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u},
+    {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u},
+    {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u},
+    {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u},
+    {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u},
+    {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u},
+    {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u},
+    {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u},
+    {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u},
+    {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u},
+    {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u},
+    {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u},
+    {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u},
+    {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u},
+    {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u},
+    {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u},
+    {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u},
+    {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u},
+    {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u},
+    {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u},
+    {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u},
+    {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u},
+    {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u},
+    {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u},
+    {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u},
+    {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u},
+    {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u},
+    {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u},
+    {30, 0xfffffffcu}};
 
 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},
+        {0x04, 0},
+        {0x05, 0},
+        {0x07, 0},
+        {0x08, 0},
+        {0x0b, 0},
+        {0x0c, 0},
+        {0x10, 0},
+        {0x13, 0},
+        {0x19, 0},
+        {0x1c, 0},
+        {0x20, 0},
+        {0x23, 0},
+        {0x2a, 0},
+        {0x31, 0},
+        {0x39, 0},
+        {0x4040, 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},
+        {0xc000, 48},
+        {0xc000, 49},
+        {0xc000, 50},
+        {0xc000, 97},
+        {0xc000, 99},
+        {0xc000, 101},
+        {0xc000, 105},
+        {0xc000, 111},
+        {0xc000, 115},
+        {0xc000, 116},
+        {0x0d, 0},
+        {0x0e, 0},
+        {0x11, 0},
+        {0x12, 0},
+        {0x14, 0},
+        {0x15, 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},
+        {0x8001, 48},
+        {0xc016, 48},
+        {0x8001, 49},
+        {0xc016, 49},
+        {0x8001, 50},
+        {0xc016, 50},
+        {0x8001, 97},
+        {0xc016, 97},
+        {0x8001, 99},
+        {0xc016, 99},
+        {0x8001, 101},
+        {0xc016, 101},
+        {0x8001, 105},
+        {0xc016, 105},
+        {0x8001, 111},
+        {0xc016, 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},
+        {0x8002, 48},
+        {0x8009, 48},
+        {0x8017, 48},
+        {0xc028, 48},
+        {0x8002, 49},
+        {0x8009, 49},
+        {0x8017, 49},
+        {0xc028, 49},
+        {0x8002, 50},
+        {0x8009, 50},
+        {0x8017, 50},
+        {0xc028, 50},
+        {0x8002, 97},
+        {0x8009, 97},
+        {0x8017, 97},
+        {0xc028, 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},
+        {0x8003, 48},
+        {0x8006, 48},
+        {0x800a, 48},
+        {0x800f, 48},
+        {0x8018, 48},
+        {0x801f, 48},
+        {0x8029, 48},
+        {0xc038, 48},
+        {0x8003, 49},
+        {0x8006, 49},
+        {0x800a, 49},
+        {0x800f, 49},
+        {0x8018, 49},
+        {0x801f, 49},
+        {0x8029, 49},
+        {0xc038, 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},
+        {0x8003, 50},
+        {0x8006, 50},
+        {0x800a, 50},
+        {0x800f, 50},
+        {0x8018, 50},
+        {0x801f, 50},
+        {0x8029, 50},
+        {0xc038, 50},
+        {0x8003, 97},
+        {0x8006, 97},
+        {0x800a, 97},
+        {0x800f, 97},
+        {0x8018, 97},
+        {0x801f, 97},
+        {0x8029, 97},
+        {0xc038, 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},
+        {0x8002, 99},
+        {0x8009, 99},
+        {0x8017, 99},
+        {0xc028, 99},
+        {0x8002, 101},
+        {0x8009, 101},
+        {0x8017, 101},
+        {0xc028, 101},
+        {0x8002, 105},
+        {0x8009, 105},
+        {0x8017, 105},
+        {0xc028, 105},
+        {0x8002, 111},
+        {0x8009, 111},
+        {0x8017, 111},
+        {0xc028, 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},
+        {0x8003, 99},
+        {0x8006, 99},
+        {0x800a, 99},
+        {0x800f, 99},
+        {0x8018, 99},
+        {0x801f, 99},
+        {0x8029, 99},
+        {0xc038, 99},
+        {0x8003, 101},
+        {0x8006, 101},
+        {0x800a, 101},
+        {0x800f, 101},
+        {0x8018, 101},
+        {0x801f, 101},
+        {0x8029, 101},
+        {0xc038, 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},
+        {0x8003, 105},
+        {0x8006, 105},
+        {0x800a, 105},
+        {0x800f, 105},
+        {0x8018, 105},
+        {0x801f, 105},
+        {0x8029, 105},
+        {0xc038, 105},
+        {0x8003, 111},
+        {0x8006, 111},
+        {0x800a, 111},
+        {0x800f, 111},
+        {0x8018, 111},
+        {0x801f, 111},
+        {0x8029, 111},
+        {0xc038, 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},
+        {0x8001, 115},
+        {0xc016, 115},
+        {0x8001, 116},
+        {0xc016, 116},
+        {0xc000, 32},
+        {0xc000, 37},
+        {0xc000, 45},
+        {0xc000, 46},
+        {0xc000, 47},
+        {0xc000, 51},
+        {0xc000, 52},
+        {0xc000, 53},
+        {0xc000, 54},
+        {0xc000, 55},
+        {0xc000, 56},
+        {0xc000, 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},
+        {0x8002, 115},
+        {0x8009, 115},
+        {0x8017, 115},
+        {0xc028, 115},
+        {0x8002, 116},
+        {0x8009, 116},
+        {0x8017, 116},
+        {0xc028, 116},
+        {0x8001, 32},
+        {0xc016, 32},
+        {0x8001, 37},
+        {0xc016, 37},
+        {0x8001, 45},
+        {0xc016, 45},
+        {0x8001, 46},
+        {0xc016, 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},
+        {0x8003, 115},
+        {0x8006, 115},
+        {0x800a, 115},
+        {0x800f, 115},
+        {0x8018, 115},
+        {0x801f, 115},
+        {0x8029, 115},
+        {0xc038, 115},
+        {0x8003, 116},
+        {0x8006, 116},
+        {0x800a, 116},
+        {0x800f, 116},
+        {0x8018, 116},
+        {0x801f, 116},
+        {0x8029, 116},
+        {0xc038, 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},
+        {0x8002, 32},
+        {0x8009, 32},
+        {0x8017, 32},
+        {0xc028, 32},
+        {0x8002, 37},
+        {0x8009, 37},
+        {0x8017, 37},
+        {0xc028, 37},
+        {0x8002, 45},
+        {0x8009, 45},
+        {0x8017, 45},
+        {0xc028, 45},
+        {0x8002, 46},
+        {0x8009, 46},
+        {0x8017, 46},
+        {0xc028, 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},
+        {0x8003, 32},
+        {0x8006, 32},
+        {0x800a, 32},
+        {0x800f, 32},
+        {0x8018, 32},
+        {0x801f, 32},
+        {0x8029, 32},
+        {0xc038, 32},
+        {0x8003, 37},
+        {0x8006, 37},
+        {0x800a, 37},
+        {0x800f, 37},
+        {0x8018, 37},
+        {0x801f, 37},
+        {0x8029, 37},
+        {0xc038, 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},
+        {0x8003, 45},
+        {0x8006, 45},
+        {0x800a, 45},
+        {0x800f, 45},
+        {0x8018, 45},
+        {0x801f, 45},
+        {0x8029, 45},
+        {0xc038, 45},
+        {0x8003, 46},
+        {0x8006, 46},
+        {0x800a, 46},
+        {0x800f, 46},
+        {0x8018, 46},
+        {0x801f, 46},
+        {0x8029, 46},
+        {0xc038, 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},
+        {0x8001, 47},
+        {0xc016, 47},
+        {0x8001, 51},
+        {0xc016, 51},
+        {0x8001, 52},
+        {0xc016, 52},
+        {0x8001, 53},
+        {0xc016, 53},
+        {0x8001, 54},
+        {0xc016, 54},
+        {0x8001, 55},
+        {0xc016, 55},
+        {0x8001, 56},
+        {0xc016, 56},
+        {0x8001, 57},
+        {0xc016, 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},
+        {0x8002, 47},
+        {0x8009, 47},
+        {0x8017, 47},
+        {0xc028, 47},
+        {0x8002, 51},
+        {0x8009, 51},
+        {0x8017, 51},
+        {0xc028, 51},
+        {0x8002, 52},
+        {0x8009, 52},
+        {0x8017, 52},
+        {0xc028, 52},
+        {0x8002, 53},
+        {0x8009, 53},
+        {0x8017, 53},
+        {0xc028, 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},
+        {0x8003, 47},
+        {0x8006, 47},
+        {0x800a, 47},
+        {0x800f, 47},
+        {0x8018, 47},
+        {0x801f, 47},
+        {0x8029, 47},
+        {0xc038, 47},
+        {0x8003, 51},
+        {0x8006, 51},
+        {0x800a, 51},
+        {0x800f, 51},
+        {0x8018, 51},
+        {0x801f, 51},
+        {0x8029, 51},
+        {0xc038, 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},
+        {0x8003, 52},
+        {0x8006, 52},
+        {0x800a, 52},
+        {0x800f, 52},
+        {0x8018, 52},
+        {0x801f, 52},
+        {0x8029, 52},
+        {0xc038, 52},
+        {0x8003, 53},
+        {0x8006, 53},
+        {0x800a, 53},
+        {0x800f, 53},
+        {0x8018, 53},
+        {0x801f, 53},
+        {0x8029, 53},
+        {0xc038, 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},
+        {0x8002, 54},
+        {0x8009, 54},
+        {0x8017, 54},
+        {0xc028, 54},
+        {0x8002, 55},
+        {0x8009, 55},
+        {0x8017, 55},
+        {0xc028, 55},
+        {0x8002, 56},
+        {0x8009, 56},
+        {0x8017, 56},
+        {0xc028, 56},
+        {0x8002, 57},
+        {0x8009, 57},
+        {0x8017, 57},
+        {0xc028, 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},
+        {0x8003, 54},
+        {0x8006, 54},
+        {0x800a, 54},
+        {0x800f, 54},
+        {0x8018, 54},
+        {0x801f, 54},
+        {0x8029, 54},
+        {0xc038, 54},
+        {0x8003, 55},
+        {0x8006, 55},
+        {0x800a, 55},
+        {0x800f, 55},
+        {0x8018, 55},
+        {0x801f, 55},
+        {0x8029, 55},
+        {0xc038, 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},
+        {0x8003, 56},
+        {0x8006, 56},
+        {0x800a, 56},
+        {0x800f, 56},
+        {0x8018, 56},
+        {0x801f, 56},
+        {0x8029, 56},
+        {0xc038, 56},
+        {0x8003, 57},
+        {0x8006, 57},
+        {0x800a, 57},
+        {0x800f, 57},
+        {0x8018, 57},
+        {0x801f, 57},
+        {0x8029, 57},
+        {0xc038, 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},
+        {0x1a, 0},
+        {0x1b, 0},
+        {0x1d, 0},
+        {0x1e, 0},
+        {0x21, 0},
+        {0x22, 0},
+        {0x24, 0},
+        {0x25, 0},
+        {0x2b, 0},
+        {0x2e, 0},
+        {0x32, 0},
+        {0x35, 0},
+        {0x3a, 0},
+        {0x3d, 0},
+        {0x41, 0},
+        {0x4044, 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},
+        {0xc000, 61},
+        {0xc000, 65},
+        {0xc000, 95},
+        {0xc000, 98},
+        {0xc000, 100},
+        {0xc000, 102},
+        {0xc000, 103},
+        {0xc000, 104},
+        {0xc000, 108},
+        {0xc000, 109},
+        {0xc000, 110},
+        {0xc000, 112},
+        {0xc000, 114},
+        {0xc000, 117},
+        {0x26, 0},
+        {0x27, 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},
+        {0x8001, 61},
+        {0xc016, 61},
+        {0x8001, 65},
+        {0xc016, 65},
+        {0x8001, 95},
+        {0xc016, 95},
+        {0x8001, 98},
+        {0xc016, 98},
+        {0x8001, 100},
+        {0xc016, 100},
+        {0x8001, 102},
+        {0xc016, 102},
+        {0x8001, 103},
+        {0xc016, 103},
+        {0x8001, 104},
+        {0xc016, 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},
+        {0x8002, 61},
+        {0x8009, 61},
+        {0x8017, 61},
+        {0xc028, 61},
+        {0x8002, 65},
+        {0x8009, 65},
+        {0x8017, 65},
+        {0xc028, 65},
+        {0x8002, 95},
+        {0x8009, 95},
+        {0x8017, 95},
+        {0xc028, 95},
+        {0x8002, 98},
+        {0x8009, 98},
+        {0x8017, 98},
+        {0xc028, 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},
+        {0x8003, 61},
+        {0x8006, 61},
+        {0x800a, 61},
+        {0x800f, 61},
+        {0x8018, 61},
+        {0x801f, 61},
+        {0x8029, 61},
+        {0xc038, 61},
+        {0x8003, 65},
+        {0x8006, 65},
+        {0x800a, 65},
+        {0x800f, 65},
+        {0x8018, 65},
+        {0x801f, 65},
+        {0x8029, 65},
+        {0xc038, 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},
+        {0x8003, 95},
+        {0x8006, 95},
+        {0x800a, 95},
+        {0x800f, 95},
+        {0x8018, 95},
+        {0x801f, 95},
+        {0x8029, 95},
+        {0xc038, 95},
+        {0x8003, 98},
+        {0x8006, 98},
+        {0x800a, 98},
+        {0x800f, 98},
+        {0x8018, 98},
+        {0x801f, 98},
+        {0x8029, 98},
+        {0xc038, 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},
+        {0x8002, 100},
+        {0x8009, 100},
+        {0x8017, 100},
+        {0xc028, 100},
+        {0x8002, 102},
+        {0x8009, 102},
+        {0x8017, 102},
+        {0xc028, 102},
+        {0x8002, 103},
+        {0x8009, 103},
+        {0x8017, 103},
+        {0xc028, 103},
+        {0x8002, 104},
+        {0x8009, 104},
+        {0x8017, 104},
+        {0xc028, 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},
+        {0x8003, 100},
+        {0x8006, 100},
+        {0x800a, 100},
+        {0x800f, 100},
+        {0x8018, 100},
+        {0x801f, 100},
+        {0x8029, 100},
+        {0xc038, 100},
+        {0x8003, 102},
+        {0x8006, 102},
+        {0x800a, 102},
+        {0x800f, 102},
+        {0x8018, 102},
+        {0x801f, 102},
+        {0x8029, 102},
+        {0xc038, 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},
+        {0x8003, 103},
+        {0x8006, 103},
+        {0x800a, 103},
+        {0x800f, 103},
+        {0x8018, 103},
+        {0x801f, 103},
+        {0x8029, 103},
+        {0xc038, 103},
+        {0x8003, 104},
+        {0x8006, 104},
+        {0x800a, 104},
+        {0x800f, 104},
+        {0x8018, 104},
+        {0x801f, 104},
+        {0x8029, 104},
+        {0xc038, 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},
+        {0x8001, 108},
+        {0xc016, 108},
+        {0x8001, 109},
+        {0xc016, 109},
+        {0x8001, 110},
+        {0xc016, 110},
+        {0x8001, 112},
+        {0xc016, 112},
+        {0x8001, 114},
+        {0xc016, 114},
+        {0x8001, 117},
+        {0xc016, 117},
+        {0xc000, 58},
+        {0xc000, 66},
+        {0xc000, 67},
+        {0xc000, 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},
+        {0x8002, 108},
+        {0x8009, 108},
+        {0x8017, 108},
+        {0xc028, 108},
+        {0x8002, 109},
+        {0x8009, 109},
+        {0x8017, 109},
+        {0xc028, 109},
+        {0x8002, 110},
+        {0x8009, 110},
+        {0x8017, 110},
+        {0xc028, 110},
+        {0x8002, 112},
+        {0x8009, 112},
+        {0x8017, 112},
+        {0xc028, 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},
+        {0x8003, 108},
+        {0x8006, 108},
+        {0x800a, 108},
+        {0x800f, 108},
+        {0x8018, 108},
+        {0x801f, 108},
+        {0x8029, 108},
+        {0xc038, 108},
+        {0x8003, 109},
+        {0x8006, 109},
+        {0x800a, 109},
+        {0x800f, 109},
+        {0x8018, 109},
+        {0x801f, 109},
+        {0x8029, 109},
+        {0xc038, 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},
+        {0x8003, 110},
+        {0x8006, 110},
+        {0x800a, 110},
+        {0x800f, 110},
+        {0x8018, 110},
+        {0x801f, 110},
+        {0x8029, 110},
+        {0xc038, 110},
+        {0x8003, 112},
+        {0x8006, 112},
+        {0x800a, 112},
+        {0x800f, 112},
+        {0x8018, 112},
+        {0x801f, 112},
+        {0x8029, 112},
+        {0xc038, 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},
+        {0x8002, 114},
+        {0x8009, 114},
+        {0x8017, 114},
+        {0xc028, 114},
+        {0x8002, 117},
+        {0x8009, 117},
+        {0x8017, 117},
+        {0xc028, 117},
+        {0x8001, 58},
+        {0xc016, 58},
+        {0x8001, 66},
+        {0xc016, 66},
+        {0x8001, 67},
+        {0xc016, 67},
+        {0x8001, 68},
+        {0xc016, 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},
+        {0x8003, 114},
+        {0x8006, 114},
+        {0x800a, 114},
+        {0x800f, 114},
+        {0x8018, 114},
+        {0x801f, 114},
+        {0x8029, 114},
+        {0xc038, 114},
+        {0x8003, 117},
+        {0x8006, 117},
+        {0x800a, 117},
+        {0x800f, 117},
+        {0x8018, 117},
+        {0x801f, 117},
+        {0x8029, 117},
+        {0xc038, 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},
+        {0x8002, 58},
+        {0x8009, 58},
+        {0x8017, 58},
+        {0xc028, 58},
+        {0x8002, 66},
+        {0x8009, 66},
+        {0x8017, 66},
+        {0xc028, 66},
+        {0x8002, 67},
+        {0x8009, 67},
+        {0x8017, 67},
+        {0xc028, 67},
+        {0x8002, 68},
+        {0x8009, 68},
+        {0x8017, 68},
+        {0xc028, 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},
+        {0x8003, 58},
+        {0x8006, 58},
+        {0x800a, 58},
+        {0x800f, 58},
+        {0x8018, 58},
+        {0x801f, 58},
+        {0x8029, 58},
+        {0xc038, 58},
+        {0x8003, 66},
+        {0x8006, 66},
+        {0x800a, 66},
+        {0x800f, 66},
+        {0x8018, 66},
+        {0x801f, 66},
+        {0x8029, 66},
+        {0xc038, 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},
+        {0x8003, 67},
+        {0x8006, 67},
+        {0x800a, 67},
+        {0x800f, 67},
+        {0x8018, 67},
+        {0x801f, 67},
+        {0x8029, 67},
+        {0xc038, 67},
+        {0x8003, 68},
+        {0x8006, 68},
+        {0x800a, 68},
+        {0x800f, 68},
+        {0x8018, 68},
+        {0x801f, 68},
+        {0x8029, 68},
+        {0xc038, 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},
+        {0x2c, 0},
+        {0x2d, 0},
+        {0x2f, 0},
+        {0x30, 0},
+        {0x33, 0},
+        {0x34, 0},
+        {0x36, 0},
+        {0x37, 0},
+        {0x3b, 0},
+        {0x3c, 0},
+        {0x3e, 0},
+        {0x3f, 0},
+        {0x42, 0},
+        {0x43, 0},
+        {0x45, 0},
+        {0x4048, 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},
+        {0xc000, 69},
+        {0xc000, 70},
+        {0xc000, 71},
+        {0xc000, 72},
+        {0xc000, 73},
+        {0xc000, 74},
+        {0xc000, 75},
+        {0xc000, 76},
+        {0xc000, 77},
+        {0xc000, 78},
+        {0xc000, 79},
+        {0xc000, 80},
+        {0xc000, 81},
+        {0xc000, 82},
+        {0xc000, 83},
+        {0xc000, 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},
+        {0x8001, 69},
+        {0xc016, 69},
+        {0x8001, 70},
+        {0xc016, 70},
+        {0x8001, 71},
+        {0xc016, 71},
+        {0x8001, 72},
+        {0xc016, 72},
+        {0x8001, 73},
+        {0xc016, 73},
+        {0x8001, 74},
+        {0xc016, 74},
+        {0x8001, 75},
+        {0xc016, 75},
+        {0x8001, 76},
+        {0xc016, 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},
+        {0x8002, 69},
+        {0x8009, 69},
+        {0x8017, 69},
+        {0xc028, 69},
+        {0x8002, 70},
+        {0x8009, 70},
+        {0x8017, 70},
+        {0xc028, 70},
+        {0x8002, 71},
+        {0x8009, 71},
+        {0x8017, 71},
+        {0xc028, 71},
+        {0x8002, 72},
+        {0x8009, 72},
+        {0x8017, 72},
+        {0xc028, 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},
+        {0x8003, 69},
+        {0x8006, 69},
+        {0x800a, 69},
+        {0x800f, 69},
+        {0x8018, 69},
+        {0x801f, 69},
+        {0x8029, 69},
+        {0xc038, 69},
+        {0x8003, 70},
+        {0x8006, 70},
+        {0x800a, 70},
+        {0x800f, 70},
+        {0x8018, 70},
+        {0x801f, 70},
+        {0x8029, 70},
+        {0xc038, 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},
+        {0x8003, 71},
+        {0x8006, 71},
+        {0x800a, 71},
+        {0x800f, 71},
+        {0x8018, 71},
+        {0x801f, 71},
+        {0x8029, 71},
+        {0xc038, 71},
+        {0x8003, 72},
+        {0x8006, 72},
+        {0x800a, 72},
+        {0x800f, 72},
+        {0x8018, 72},
+        {0x801f, 72},
+        {0x8029, 72},
+        {0xc038, 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},
+        {0x8002, 73},
+        {0x8009, 73},
+        {0x8017, 73},
+        {0xc028, 73},
+        {0x8002, 74},
+        {0x8009, 74},
+        {0x8017, 74},
+        {0xc028, 74},
+        {0x8002, 75},
+        {0x8009, 75},
+        {0x8017, 75},
+        {0xc028, 75},
+        {0x8002, 76},
+        {0x8009, 76},
+        {0x8017, 76},
+        {0xc028, 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},
+        {0x8003, 73},
+        {0x8006, 73},
+        {0x800a, 73},
+        {0x800f, 73},
+        {0x8018, 73},
+        {0x801f, 73},
+        {0x8029, 73},
+        {0xc038, 73},
+        {0x8003, 74},
+        {0x8006, 74},
+        {0x800a, 74},
+        {0x800f, 74},
+        {0x8018, 74},
+        {0x801f, 74},
+        {0x8029, 74},
+        {0xc038, 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},
+        {0x8003, 75},
+        {0x8006, 75},
+        {0x800a, 75},
+        {0x800f, 75},
+        {0x8018, 75},
+        {0x801f, 75},
+        {0x8029, 75},
+        {0xc038, 75},
+        {0x8003, 76},
+        {0x8006, 76},
+        {0x800a, 76},
+        {0x800f, 76},
+        {0x8018, 76},
+        {0x801f, 76},
+        {0x8029, 76},
+        {0xc038, 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},
+        {0x8001, 77},
+        {0xc016, 77},
+        {0x8001, 78},
+        {0xc016, 78},
+        {0x8001, 79},
+        {0xc016, 79},
+        {0x8001, 80},
+        {0xc016, 80},
+        {0x8001, 81},
+        {0xc016, 81},
+        {0x8001, 82},
+        {0xc016, 82},
+        {0x8001, 83},
+        {0xc016, 83},
+        {0x8001, 84},
+        {0xc016, 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},
+        {0x8002, 77},
+        {0x8009, 77},
+        {0x8017, 77},
+        {0xc028, 77},
+        {0x8002, 78},
+        {0x8009, 78},
+        {0x8017, 78},
+        {0xc028, 78},
+        {0x8002, 79},
+        {0x8009, 79},
+        {0x8017, 79},
+        {0xc028, 79},
+        {0x8002, 80},
+        {0x8009, 80},
+        {0x8017, 80},
+        {0xc028, 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},
+        {0x8003, 77},
+        {0x8006, 77},
+        {0x800a, 77},
+        {0x800f, 77},
+        {0x8018, 77},
+        {0x801f, 77},
+        {0x8029, 77},
+        {0xc038, 77},
+        {0x8003, 78},
+        {0x8006, 78},
+        {0x800a, 78},
+        {0x800f, 78},
+        {0x8018, 78},
+        {0x801f, 78},
+        {0x8029, 78},
+        {0xc038, 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},
+        {0x8003, 79},
+        {0x8006, 79},
+        {0x800a, 79},
+        {0x800f, 79},
+        {0x8018, 79},
+        {0x801f, 79},
+        {0x8029, 79},
+        {0xc038, 79},
+        {0x8003, 80},
+        {0x8006, 80},
+        {0x800a, 80},
+        {0x800f, 80},
+        {0x8018, 80},
+        {0x801f, 80},
+        {0x8029, 80},
+        {0xc038, 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},
+        {0x8002, 81},
+        {0x8009, 81},
+        {0x8017, 81},
+        {0xc028, 81},
+        {0x8002, 82},
+        {0x8009, 82},
+        {0x8017, 82},
+        {0xc028, 82},
+        {0x8002, 83},
+        {0x8009, 83},
+        {0x8017, 83},
+        {0xc028, 83},
+        {0x8002, 84},
+        {0x8009, 84},
+        {0x8017, 84},
+        {0xc028, 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},
+        {0x8003, 81},
+        {0x8006, 81},
+        {0x800a, 81},
+        {0x800f, 81},
+        {0x8018, 81},
+        {0x801f, 81},
+        {0x8029, 81},
+        {0xc038, 81},
+        {0x8003, 82},
+        {0x8006, 82},
+        {0x800a, 82},
+        {0x800f, 82},
+        {0x8018, 82},
+        {0x801f, 82},
+        {0x8029, 82},
+        {0xc038, 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},
+        {0x8003, 83},
+        {0x8006, 83},
+        {0x800a, 83},
+        {0x800f, 83},
+        {0x8018, 83},
+        {0x801f, 83},
+        {0x8029, 83},
+        {0xc038, 83},
+        {0x8003, 84},
+        {0x8006, 84},
+        {0x800a, 84},
+        {0x800f, 84},
+        {0x8018, 84},
+        {0x801f, 84},
+        {0x8029, 84},
+        {0xc038, 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},
+        {0xc000, 85},
+        {0xc000, 86},
+        {0xc000, 87},
+        {0xc000, 89},
+        {0xc000, 106},
+        {0xc000, 107},
+        {0xc000, 113},
+        {0xc000, 118},
+        {0xc000, 119},
+        {0xc000, 120},
+        {0xc000, 121},
+        {0xc000, 122},
+        {0x46, 0},
+        {0x47, 0},
+        {0x49, 0},
+        {0x404a, 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},
+        {0x8001, 85},
+        {0xc016, 85},
+        {0x8001, 86},
+        {0xc016, 86},
+        {0x8001, 87},
+        {0xc016, 87},
+        {0x8001, 89},
+        {0xc016, 89},
+        {0x8001, 106},
+        {0xc016, 106},
+        {0x8001, 107},
+        {0xc016, 107},
+        {0x8001, 113},
+        {0xc016, 113},
+        {0x8001, 118},
+        {0xc016, 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},
+        {0x8002, 85},
+        {0x8009, 85},
+        {0x8017, 85},
+        {0xc028, 85},
+        {0x8002, 86},
+        {0x8009, 86},
+        {0x8017, 86},
+        {0xc028, 86},
+        {0x8002, 87},
+        {0x8009, 87},
+        {0x8017, 87},
+        {0xc028, 87},
+        {0x8002, 89},
+        {0x8009, 89},
+        {0x8017, 89},
+        {0xc028, 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},
+        {0x8003, 85},
+        {0x8006, 85},
+        {0x800a, 85},
+        {0x800f, 85},
+        {0x8018, 85},
+        {0x801f, 85},
+        {0x8029, 85},
+        {0xc038, 85},
+        {0x8003, 86},
+        {0x8006, 86},
+        {0x800a, 86},
+        {0x800f, 86},
+        {0x8018, 86},
+        {0x801f, 86},
+        {0x8029, 86},
+        {0xc038, 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},
+        {0x8003, 87},
+        {0x8006, 87},
+        {0x800a, 87},
+        {0x800f, 87},
+        {0x8018, 87},
+        {0x801f, 87},
+        {0x8029, 87},
+        {0xc038, 87},
+        {0x8003, 89},
+        {0x8006, 89},
+        {0x800a, 89},
+        {0x800f, 89},
+        {0x8018, 89},
+        {0x801f, 89},
+        {0x8029, 89},
+        {0xc038, 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},
+        {0x8002, 106},
+        {0x8009, 106},
+        {0x8017, 106},
+        {0xc028, 106},
+        {0x8002, 107},
+        {0x8009, 107},
+        {0x8017, 107},
+        {0xc028, 107},
+        {0x8002, 113},
+        {0x8009, 113},
+        {0x8017, 113},
+        {0xc028, 113},
+        {0x8002, 118},
+        {0x8009, 118},
+        {0x8017, 118},
+        {0xc028, 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},
+        {0x8003, 106},
+        {0x8006, 106},
+        {0x800a, 106},
+        {0x800f, 106},
+        {0x8018, 106},
+        {0x801f, 106},
+        {0x8029, 106},
+        {0xc038, 106},
+        {0x8003, 107},
+        {0x8006, 107},
+        {0x800a, 107},
+        {0x800f, 107},
+        {0x8018, 107},
+        {0x801f, 107},
+        {0x8029, 107},
+        {0xc038, 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},
+        {0x8003, 113},
+        {0x8006, 113},
+        {0x800a, 113},
+        {0x800f, 113},
+        {0x8018, 113},
+        {0x801f, 113},
+        {0x8029, 113},
+        {0xc038, 113},
+        {0x8003, 118},
+        {0x8006, 118},
+        {0x800a, 118},
+        {0x800f, 118},
+        {0x8018, 118},
+        {0x801f, 118},
+        {0x8029, 118},
+        {0xc038, 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},
+        {0x8001, 119},
+        {0xc016, 119},
+        {0x8001, 120},
+        {0xc016, 120},
+        {0x8001, 121},
+        {0xc016, 121},
+        {0x8001, 122},
+        {0xc016, 122},
+        {0xc000, 38},
+        {0xc000, 42},
+        {0xc000, 44},
+        {0xc000, 59},
+        {0xc000, 88},
+        {0xc000, 90},
+        {0x4b, 0},
+        {0x4e, 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},
+        {0x8002, 119},
+        {0x8009, 119},
+        {0x8017, 119},
+        {0xc028, 119},
+        {0x8002, 120},
+        {0x8009, 120},
+        {0x8017, 120},
+        {0xc028, 120},
+        {0x8002, 121},
+        {0x8009, 121},
+        {0x8017, 121},
+        {0xc028, 121},
+        {0x8002, 122},
+        {0x8009, 122},
+        {0x8017, 122},
+        {0xc028, 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},
+        {0x8003, 119},
+        {0x8006, 119},
+        {0x800a, 119},
+        {0x800f, 119},
+        {0x8018, 119},
+        {0x801f, 119},
+        {0x8029, 119},
+        {0xc038, 119},
+        {0x8003, 120},
+        {0x8006, 120},
+        {0x800a, 120},
+        {0x800f, 120},
+        {0x8018, 120},
+        {0x801f, 120},
+        {0x8029, 120},
+        {0xc038, 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},
+        {0x8003, 121},
+        {0x8006, 121},
+        {0x800a, 121},
+        {0x800f, 121},
+        {0x8018, 121},
+        {0x801f, 121},
+        {0x8029, 121},
+        {0xc038, 121},
+        {0x8003, 122},
+        {0x8006, 122},
+        {0x800a, 122},
+        {0x800f, 122},
+        {0x8018, 122},
+        {0x801f, 122},
+        {0x8029, 122},
+        {0xc038, 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},
+        {0x8001, 38},
+        {0xc016, 38},
+        {0x8001, 42},
+        {0xc016, 42},
+        {0x8001, 44},
+        {0xc016, 44},
+        {0x8001, 59},
+        {0xc016, 59},
+        {0x8001, 88},
+        {0xc016, 88},
+        {0x8001, 90},
+        {0xc016, 90},
+        {0x4c, 0},
+        {0x4d, 0},
+        {0x4f, 0},
+        {0x51, 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},
+        {0x8002, 38},
+        {0x8009, 38},
+        {0x8017, 38},
+        {0xc028, 38},
+        {0x8002, 42},
+        {0x8009, 42},
+        {0x8017, 42},
+        {0xc028, 42},
+        {0x8002, 44},
+        {0x8009, 44},
+        {0x8017, 44},
+        {0xc028, 44},
+        {0x8002, 59},
+        {0x8009, 59},
+        {0x8017, 59},
+        {0xc028, 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},
+        {0x8003, 38},
+        {0x8006, 38},
+        {0x800a, 38},
+        {0x800f, 38},
+        {0x8018, 38},
+        {0x801f, 38},
+        {0x8029, 38},
+        {0xc038, 38},
+        {0x8003, 42},
+        {0x8006, 42},
+        {0x800a, 42},
+        {0x800f, 42},
+        {0x8018, 42},
+        {0x801f, 42},
+        {0x8029, 42},
+        {0xc038, 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},
+        {0x8003, 44},
+        {0x8006, 44},
+        {0x800a, 44},
+        {0x800f, 44},
+        {0x8018, 44},
+        {0x801f, 44},
+        {0x8029, 44},
+        {0xc038, 44},
+        {0x8003, 59},
+        {0x8006, 59},
+        {0x800a, 59},
+        {0x800f, 59},
+        {0x8018, 59},
+        {0x801f, 59},
+        {0x8029, 59},
+        {0xc038, 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},
+        {0x8002, 88},
+        {0x8009, 88},
+        {0x8017, 88},
+        {0xc028, 88},
+        {0x8002, 90},
+        {0x8009, 90},
+        {0x8017, 90},
+        {0xc028, 90},
+        {0xc000, 33},
+        {0xc000, 34},
+        {0xc000, 40},
+        {0xc000, 41},
+        {0xc000, 63},
+        {0x50, 0},
+        {0x52, 0},
+        {0x54, 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},
+        {0x8003, 88},
+        {0x8006, 88},
+        {0x800a, 88},
+        {0x800f, 88},
+        {0x8018, 88},
+        {0x801f, 88},
+        {0x8029, 88},
+        {0xc038, 88},
+        {0x8003, 90},
+        {0x8006, 90},
+        {0x800a, 90},
+        {0x800f, 90},
+        {0x8018, 90},
+        {0x801f, 90},
+        {0x8029, 90},
+        {0xc038, 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},
+        {0x8001, 33},
+        {0xc016, 33},
+        {0x8001, 34},
+        {0xc016, 34},
+        {0x8001, 40},
+        {0xc016, 40},
+        {0x8001, 41},
+        {0xc016, 41},
+        {0x8001, 63},
+        {0xc016, 63},
+        {0xc000, 39},
+        {0xc000, 43},
+        {0xc000, 124},
+        {0x53, 0},
+        {0x55, 0},
+        {0x58, 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},
+        {0x8002, 33},
+        {0x8009, 33},
+        {0x8017, 33},
+        {0xc028, 33},
+        {0x8002, 34},
+        {0x8009, 34},
+        {0x8017, 34},
+        {0xc028, 34},
+        {0x8002, 40},
+        {0x8009, 40},
+        {0x8017, 40},
+        {0xc028, 40},
+        {0x8002, 41},
+        {0x8009, 41},
+        {0x8017, 41},
+        {0xc028, 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},
+        {0x8003, 33},
+        {0x8006, 33},
+        {0x800a, 33},
+        {0x800f, 33},
+        {0x8018, 33},
+        {0x801f, 33},
+        {0x8029, 33},
+        {0xc038, 33},
+        {0x8003, 34},
+        {0x8006, 34},
+        {0x800a, 34},
+        {0x800f, 34},
+        {0x8018, 34},
+        {0x801f, 34},
+        {0x8029, 34},
+        {0xc038, 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},
+        {0x8003, 40},
+        {0x8006, 40},
+        {0x800a, 40},
+        {0x800f, 40},
+        {0x8018, 40},
+        {0x801f, 40},
+        {0x8029, 40},
+        {0xc038, 40},
+        {0x8003, 41},
+        {0x8006, 41},
+        {0x800a, 41},
+        {0x800f, 41},
+        {0x8018, 41},
+        {0x801f, 41},
+        {0x8029, 41},
+        {0xc038, 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},
+        {0x8002, 63},
+        {0x8009, 63},
+        {0x8017, 63},
+        {0xc028, 63},
+        {0x8001, 39},
+        {0xc016, 39},
+        {0x8001, 43},
+        {0xc016, 43},
+        {0x8001, 124},
+        {0xc016, 124},
+        {0xc000, 35},
+        {0xc000, 62},
+        {0x56, 0},
+        {0x57, 0},
+        {0x59, 0},
+        {0x5a, 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},
+        {0x8003, 63},
+        {0x8006, 63},
+        {0x800a, 63},
+        {0x800f, 63},
+        {0x8018, 63},
+        {0x801f, 63},
+        {0x8029, 63},
+        {0xc038, 63},
+        {0x8002, 39},
+        {0x8009, 39},
+        {0x8017, 39},
+        {0xc028, 39},
+        {0x8002, 43},
+        {0x8009, 43},
+        {0x8017, 43},
+        {0xc028, 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},
+        {0x8003, 39},
+        {0x8006, 39},
+        {0x800a, 39},
+        {0x800f, 39},
+        {0x8018, 39},
+        {0x801f, 39},
+        {0x8029, 39},
+        {0xc038, 39},
+        {0x8003, 43},
+        {0x8006, 43},
+        {0x800a, 43},
+        {0x800f, 43},
+        {0x8018, 43},
+        {0x801f, 43},
+        {0x8029, 43},
+        {0xc038, 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},
+        {0x8002, 124},
+        {0x8009, 124},
+        {0x8017, 124},
+        {0xc028, 124},
+        {0x8001, 35},
+        {0xc016, 35},
+        {0x8001, 62},
+        {0xc016, 62},
+        {0xc000, 0},
+        {0xc000, 36},
+        {0xc000, 64},
+        {0xc000, 91},
+        {0xc000, 93},
+        {0xc000, 126},
+        {0x5b, 0},
+        {0x5c, 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},
+        {0x8003, 124},
+        {0x8006, 124},
+        {0x800a, 124},
+        {0x800f, 124},
+        {0x8018, 124},
+        {0x801f, 124},
+        {0x8029, 124},
+        {0xc038, 124},
+        {0x8002, 35},
+        {0x8009, 35},
+        {0x8017, 35},
+        {0xc028, 35},
+        {0x8002, 62},
+        {0x8009, 62},
+        {0x8017, 62},
+        {0xc028, 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},
+        {0x8003, 35},
+        {0x8006, 35},
+        {0x800a, 35},
+        {0x800f, 35},
+        {0x8018, 35},
+        {0x801f, 35},
+        {0x8029, 35},
+        {0xc038, 35},
+        {0x8003, 62},
+        {0x8006, 62},
+        {0x800a, 62},
+        {0x800f, 62},
+        {0x8018, 62},
+        {0x801f, 62},
+        {0x8029, 62},
+        {0xc038, 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},
+        {0x8001, 0},
+        {0xc016, 0},
+        {0x8001, 36},
+        {0xc016, 36},
+        {0x8001, 64},
+        {0xc016, 64},
+        {0x8001, 91},
+        {0xc016, 91},
+        {0x8001, 93},
+        {0xc016, 93},
+        {0x8001, 126},
+        {0xc016, 126},
+        {0xc000, 94},
+        {0xc000, 125},
+        {0x5d, 0},
+        {0x5e, 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},
+        {0x8002, 0},
+        {0x8009, 0},
+        {0x8017, 0},
+        {0xc028, 0},
+        {0x8002, 36},
+        {0x8009, 36},
+        {0x8017, 36},
+        {0xc028, 36},
+        {0x8002, 64},
+        {0x8009, 64},
+        {0x8017, 64},
+        {0xc028, 64},
+        {0x8002, 91},
+        {0x8009, 91},
+        {0x8017, 91},
+        {0xc028, 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},
+        {0x8003, 0},
+        {0x8006, 0},
+        {0x800a, 0},
+        {0x800f, 0},
+        {0x8018, 0},
+        {0x801f, 0},
+        {0x8029, 0},
+        {0xc038, 0},
+        {0x8003, 36},
+        {0x8006, 36},
+        {0x800a, 36},
+        {0x800f, 36},
+        {0x8018, 36},
+        {0x801f, 36},
+        {0x8029, 36},
+        {0xc038, 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},
+        {0x8003, 64},
+        {0x8006, 64},
+        {0x800a, 64},
+        {0x800f, 64},
+        {0x8018, 64},
+        {0x801f, 64},
+        {0x8029, 64},
+        {0xc038, 64},
+        {0x8003, 91},
+        {0x8006, 91},
+        {0x800a, 91},
+        {0x800f, 91},
+        {0x8018, 91},
+        {0x801f, 91},
+        {0x8029, 91},
+        {0xc038, 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},
+        {0x8002, 93},
+        {0x8009, 93},
+        {0x8017, 93},
+        {0xc028, 93},
+        {0x8002, 126},
+        {0x8009, 126},
+        {0x8017, 126},
+        {0xc028, 126},
+        {0x8001, 94},
+        {0xc016, 94},
+        {0x8001, 125},
+        {0xc016, 125},
+        {0xc000, 60},
+        {0xc000, 96},
+        {0xc000, 123},
+        {0x5f, 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},
+        {0x8003, 93},
+        {0x8006, 93},
+        {0x800a, 93},
+        {0x800f, 93},
+        {0x8018, 93},
+        {0x801f, 93},
+        {0x8029, 93},
+        {0xc038, 93},
+        {0x8003, 126},
+        {0x8006, 126},
+        {0x800a, 126},
+        {0x800f, 126},
+        {0x8018, 126},
+        {0x801f, 126},
+        {0x8029, 126},
+        {0xc038, 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},
+        {0x8002, 94},
+        {0x8009, 94},
+        {0x8017, 94},
+        {0xc028, 94},
+        {0x8002, 125},
+        {0x8009, 125},
+        {0x8017, 125},
+        {0xc028, 125},
+        {0x8001, 60},
+        {0xc016, 60},
+        {0x8001, 96},
+        {0xc016, 96},
+        {0x8001, 123},
+        {0xc016, 123},
+        {0x60, 0},
+        {0x6e, 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},
+        {0x8003, 94},
+        {0x8006, 94},
+        {0x800a, 94},
+        {0x800f, 94},
+        {0x8018, 94},
+        {0x801f, 94},
+        {0x8029, 94},
+        {0xc038, 94},
+        {0x8003, 125},
+        {0x8006, 125},
+        {0x800a, 125},
+        {0x800f, 125},
+        {0x8018, 125},
+        {0x801f, 125},
+        {0x8029, 125},
+        {0xc038, 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},
+        {0x8002, 60},
+        {0x8009, 60},
+        {0x8017, 60},
+        {0xc028, 60},
+        {0x8002, 96},
+        {0x8009, 96},
+        {0x8017, 96},
+        {0xc028, 96},
+        {0x8002, 123},
+        {0x8009, 123},
+        {0x8017, 123},
+        {0xc028, 123},
+        {0x61, 0},
+        {0x65, 0},
+        {0x6f, 0},
+        {0x85, 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},
+        {0x8003, 60},
+        {0x8006, 60},
+        {0x800a, 60},
+        {0x800f, 60},
+        {0x8018, 60},
+        {0x801f, 60},
+        {0x8029, 60},
+        {0xc038, 60},
+        {0x8003, 96},
+        {0x8006, 96},
+        {0x800a, 96},
+        {0x800f, 96},
+        {0x8018, 96},
+        {0x801f, 96},
+        {0x8029, 96},
+        {0xc038, 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},
+        {0x8003, 123},
+        {0x8006, 123},
+        {0x800a, 123},
+        {0x800f, 123},
+        {0x8018, 123},
+        {0x801f, 123},
+        {0x8029, 123},
+        {0xc038, 123},
+        {0x62, 0},
+        {0x63, 0},
+        {0x66, 0},
+        {0x69, 0},
+        {0x70, 0},
+        {0x77, 0},
+        {0x86, 0},
+        {0x99, 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},
+        {0xc000, 92},
+        {0xc000, 195},
+        {0xc000, 208},
+        {0x64, 0},
+        {0x67, 0},
+        {0x68, 0},
+        {0x6a, 0},
+        {0x6b, 0},
+        {0x71, 0},
+        {0x74, 0},
+        {0x78, 0},
+        {0x7e, 0},
+        {0x87, 0},
+        {0x8e, 0},
+        {0x9a, 0},
+        {0xa9, 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},
+        {0x8001, 92},
+        {0xc016, 92},
+        {0x8001, 195},
+        {0xc016, 195},
+        {0x8001, 208},
+        {0xc016, 208},
+        {0xc000, 128},
+        {0xc000, 130},
+        {0xc000, 131},
+        {0xc000, 162},
+        {0xc000, 184},
+        {0xc000, 194},
+        {0xc000, 224},
+        {0xc000, 226},
+        {0x6c, 0},
+        {0x6d, 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},
+        {0x8002, 92},
+        {0x8009, 92},
+        {0x8017, 92},
+        {0xc028, 92},
+        {0x8002, 195},
+        {0x8009, 195},
+        {0x8017, 195},
+        {0xc028, 195},
+        {0x8002, 208},
+        {0x8009, 208},
+        {0x8017, 208},
+        {0xc028, 208},
+        {0x8001, 128},
+        {0xc016, 128},
+        {0x8001, 130},
+        {0xc016, 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},
+        {0x8003, 92},
+        {0x8006, 92},
+        {0x800a, 92},
+        {0x800f, 92},
+        {0x8018, 92},
+        {0x801f, 92},
+        {0x8029, 92},
+        {0xc038, 92},
+        {0x8003, 195},
+        {0x8006, 195},
+        {0x800a, 195},
+        {0x800f, 195},
+        {0x8018, 195},
+        {0x801f, 195},
+        {0x8029, 195},
+        {0xc038, 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},
+        {0x8003, 208},
+        {0x8006, 208},
+        {0x800a, 208},
+        {0x800f, 208},
+        {0x8018, 208},
+        {0x801f, 208},
+        {0x8029, 208},
+        {0xc038, 208},
+        {0x8002, 128},
+        {0x8009, 128},
+        {0x8017, 128},
+        {0xc028, 128},
+        {0x8002, 130},
+        {0x8009, 130},
+        {0x8017, 130},
+        {0xc028, 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},
+        {0x8003, 128},
+        {0x8006, 128},
+        {0x800a, 128},
+        {0x800f, 128},
+        {0x8018, 128},
+        {0x801f, 128},
+        {0x8029, 128},
+        {0xc038, 128},
+        {0x8003, 130},
+        {0x8006, 130},
+        {0x800a, 130},
+        {0x800f, 130},
+        {0x8018, 130},
+        {0x801f, 130},
+        {0x8029, 130},
+        {0xc038, 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},
+        {0x8001, 131},
+        {0xc016, 131},
+        {0x8001, 162},
+        {0xc016, 162},
+        {0x8001, 184},
+        {0xc016, 184},
+        {0x8001, 194},
+        {0xc016, 194},
+        {0x8001, 224},
+        {0xc016, 224},
+        {0x8001, 226},
+        {0xc016, 226},
+        {0xc000, 153},
+        {0xc000, 161},
+        {0xc000, 167},
+        {0xc000, 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},
+        {0x8002, 131},
+        {0x8009, 131},
+        {0x8017, 131},
+        {0xc028, 131},
+        {0x8002, 162},
+        {0x8009, 162},
+        {0x8017, 162},
+        {0xc028, 162},
+        {0x8002, 184},
+        {0x8009, 184},
+        {0x8017, 184},
+        {0xc028, 184},
+        {0x8002, 194},
+        {0x8009, 194},
+        {0x8017, 194},
+        {0xc028, 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},
+        {0x8003, 131},
+        {0x8006, 131},
+        {0x800a, 131},
+        {0x800f, 131},
+        {0x8018, 131},
+        {0x801f, 131},
+        {0x8029, 131},
+        {0xc038, 131},
+        {0x8003, 162},
+        {0x8006, 162},
+        {0x800a, 162},
+        {0x800f, 162},
+        {0x8018, 162},
+        {0x801f, 162},
+        {0x8029, 162},
+        {0xc038, 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},
+        {0x8003, 184},
+        {0x8006, 184},
+        {0x800a, 184},
+        {0x800f, 184},
+        {0x8018, 184},
+        {0x801f, 184},
+        {0x8029, 184},
+        {0xc038, 184},
+        {0x8003, 194},
+        {0x8006, 194},
+        {0x800a, 194},
+        {0x800f, 194},
+        {0x8018, 194},
+        {0x801f, 194},
+        {0x8029, 194},
+        {0xc038, 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},
+        {0x8002, 224},
+        {0x8009, 224},
+        {0x8017, 224},
+        {0xc028, 224},
+        {0x8002, 226},
+        {0x8009, 226},
+        {0x8017, 226},
+        {0xc028, 226},
+        {0x8001, 153},
+        {0xc016, 153},
+        {0x8001, 161},
+        {0xc016, 161},
+        {0x8001, 167},
+        {0xc016, 167},
+        {0x8001, 172},
+        {0xc016, 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},
+        {0x8003, 224},
+        {0x8006, 224},
+        {0x800a, 224},
+        {0x800f, 224},
+        {0x8018, 224},
+        {0x801f, 224},
+        {0x8029, 224},
+        {0xc038, 224},
+        {0x8003, 226},
+        {0x8006, 226},
+        {0x800a, 226},
+        {0x800f, 226},
+        {0x8018, 226},
+        {0x801f, 226},
+        {0x8029, 226},
+        {0xc038, 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},
+        {0x8002, 153},
+        {0x8009, 153},
+        {0x8017, 153},
+        {0xc028, 153},
+        {0x8002, 161},
+        {0x8009, 161},
+        {0x8017, 161},
+        {0xc028, 161},
+        {0x8002, 167},
+        {0x8009, 167},
+        {0x8017, 167},
+        {0xc028, 167},
+        {0x8002, 172},
+        {0x8009, 172},
+        {0x8017, 172},
+        {0xc028, 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},
+        {0x8003, 153},
+        {0x8006, 153},
+        {0x800a, 153},
+        {0x800f, 153},
+        {0x8018, 153},
+        {0x801f, 153},
+        {0x8029, 153},
+        {0xc038, 153},
+        {0x8003, 161},
+        {0x8006, 161},
+        {0x800a, 161},
+        {0x800f, 161},
+        {0x8018, 161},
+        {0x801f, 161},
+        {0x8029, 161},
+        {0xc038, 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},
+        {0x8003, 167},
+        {0x8006, 167},
+        {0x800a, 167},
+        {0x800f, 167},
+        {0x8018, 167},
+        {0x801f, 167},
+        {0x8029, 167},
+        {0xc038, 167},
+        {0x8003, 172},
+        {0x8006, 172},
+        {0x800a, 172},
+        {0x800f, 172},
+        {0x8018, 172},
+        {0x801f, 172},
+        {0x8029, 172},
+        {0xc038, 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},
+        {0x72, 0},
+        {0x73, 0},
+        {0x75, 0},
+        {0x76, 0},
+        {0x79, 0},
+        {0x7b, 0},
+        {0x7f, 0},
+        {0x82, 0},
+        {0x88, 0},
+        {0x8b, 0},
+        {0x8f, 0},
+        {0x92, 0},
+        {0x9b, 0},
+        {0xa2, 0},
+        {0xaa, 0},
+        {0xb4, 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},
+        {0xc000, 176},
+        {0xc000, 177},
+        {0xc000, 179},
+        {0xc000, 209},
+        {0xc000, 216},
+        {0xc000, 217},
+        {0xc000, 227},
+        {0xc000, 229},
+        {0xc000, 230},
+        {0x7a, 0},
+        {0x7c, 0},
+        {0x7d, 0},
+        {0x80, 0},
+        {0x81, 0},
+        {0x83, 0},
+        {0x84, 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},
+        {0x8001, 176},
+        {0xc016, 176},
+        {0x8001, 177},
+        {0xc016, 177},
+        {0x8001, 179},
+        {0xc016, 179},
+        {0x8001, 209},
+        {0xc016, 209},
+        {0x8001, 216},
+        {0xc016, 216},
+        {0x8001, 217},
+        {0xc016, 217},
+        {0x8001, 227},
+        {0xc016, 227},
+        {0x8001, 229},
+        {0xc016, 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},
+        {0x8002, 176},
+        {0x8009, 176},
+        {0x8017, 176},
+        {0xc028, 176},
+        {0x8002, 177},
+        {0x8009, 177},
+        {0x8017, 177},
+        {0xc028, 177},
+        {0x8002, 179},
+        {0x8009, 179},
+        {0x8017, 179},
+        {0xc028, 179},
+        {0x8002, 209},
+        {0x8009, 209},
+        {0x8017, 209},
+        {0xc028, 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},
+        {0x8003, 176},
+        {0x8006, 176},
+        {0x800a, 176},
+        {0x800f, 176},
+        {0x8018, 176},
+        {0x801f, 176},
+        {0x8029, 176},
+        {0xc038, 176},
+        {0x8003, 177},
+        {0x8006, 177},
+        {0x800a, 177},
+        {0x800f, 177},
+        {0x8018, 177},
+        {0x801f, 177},
+        {0x8029, 177},
+        {0xc038, 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},
+        {0x8003, 179},
+        {0x8006, 179},
+        {0x800a, 179},
+        {0x800f, 179},
+        {0x8018, 179},
+        {0x801f, 179},
+        {0x8029, 179},
+        {0xc038, 179},
+        {0x8003, 209},
+        {0x8006, 209},
+        {0x800a, 209},
+        {0x800f, 209},
+        {0x8018, 209},
+        {0x801f, 209},
+        {0x8029, 209},
+        {0xc038, 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},
+        {0x8002, 216},
+        {0x8009, 216},
+        {0x8017, 216},
+        {0xc028, 216},
+        {0x8002, 217},
+        {0x8009, 217},
+        {0x8017, 217},
+        {0xc028, 217},
+        {0x8002, 227},
+        {0x8009, 227},
+        {0x8017, 227},
+        {0xc028, 227},
+        {0x8002, 229},
+        {0x8009, 229},
+        {0x8017, 229},
+        {0xc028, 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},
+        {0x8003, 216},
+        {0x8006, 216},
+        {0x800a, 216},
+        {0x800f, 216},
+        {0x8018, 216},
+        {0x801f, 216},
+        {0x8029, 216},
+        {0xc038, 216},
+        {0x8003, 217},
+        {0x8006, 217},
+        {0x800a, 217},
+        {0x800f, 217},
+        {0x8018, 217},
+        {0x801f, 217},
+        {0x8029, 217},
+        {0xc038, 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},
+        {0x8003, 227},
+        {0x8006, 227},
+        {0x800a, 227},
+        {0x800f, 227},
+        {0x8018, 227},
+        {0x801f, 227},
+        {0x8029, 227},
+        {0xc038, 227},
+        {0x8003, 229},
+        {0x8006, 229},
+        {0x800a, 229},
+        {0x800f, 229},
+        {0x8018, 229},
+        {0x801f, 229},
+        {0x8029, 229},
+        {0xc038, 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},
+        {0x8001, 230},
+        {0xc016, 230},
+        {0xc000, 129},
+        {0xc000, 132},
+        {0xc000, 133},
+        {0xc000, 134},
+        {0xc000, 136},
+        {0xc000, 146},
+        {0xc000, 154},
+        {0xc000, 156},
+        {0xc000, 160},
+        {0xc000, 163},
+        {0xc000, 164},
+        {0xc000, 169},
+        {0xc000, 170},
+        {0xc000, 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},
+        {0x8002, 230},
+        {0x8009, 230},
+        {0x8017, 230},
+        {0xc028, 230},
+        {0x8001, 129},
+        {0xc016, 129},
+        {0x8001, 132},
+        {0xc016, 132},
+        {0x8001, 133},
+        {0xc016, 133},
+        {0x8001, 134},
+        {0xc016, 134},
+        {0x8001, 136},
+        {0xc016, 136},
+        {0x8001, 146},
+        {0xc016, 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},
+        {0x8003, 230},
+        {0x8006, 230},
+        {0x800a, 230},
+        {0x800f, 230},
+        {0x8018, 230},
+        {0x801f, 230},
+        {0x8029, 230},
+        {0xc038, 230},
+        {0x8002, 129},
+        {0x8009, 129},
+        {0x8017, 129},
+        {0xc028, 129},
+        {0x8002, 132},
+        {0x8009, 132},
+        {0x8017, 132},
+        {0xc028, 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},
+        {0x8003, 129},
+        {0x8006, 129},
+        {0x800a, 129},
+        {0x800f, 129},
+        {0x8018, 129},
+        {0x801f, 129},
+        {0x8029, 129},
+        {0xc038, 129},
+        {0x8003, 132},
+        {0x8006, 132},
+        {0x800a, 132},
+        {0x800f, 132},
+        {0x8018, 132},
+        {0x801f, 132},
+        {0x8029, 132},
+        {0xc038, 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},
+        {0x8002, 133},
+        {0x8009, 133},
+        {0x8017, 133},
+        {0xc028, 133},
+        {0x8002, 134},
+        {0x8009, 134},
+        {0x8017, 134},
+        {0xc028, 134},
+        {0x8002, 136},
+        {0x8009, 136},
+        {0x8017, 136},
+        {0xc028, 136},
+        {0x8002, 146},
+        {0x8009, 146},
+        {0x8017, 146},
+        {0xc028, 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},
+        {0x8003, 133},
+        {0x8006, 133},
+        {0x800a, 133},
+        {0x800f, 133},
+        {0x8018, 133},
+        {0x801f, 133},
+        {0x8029, 133},
+        {0xc038, 133},
+        {0x8003, 134},
+        {0x8006, 134},
+        {0x800a, 134},
+        {0x800f, 134},
+        {0x8018, 134},
+        {0x801f, 134},
+        {0x8029, 134},
+        {0xc038, 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},
+        {0x8003, 136},
+        {0x8006, 136},
+        {0x800a, 136},
+        {0x800f, 136},
+        {0x8018, 136},
+        {0x801f, 136},
+        {0x8029, 136},
+        {0xc038, 136},
+        {0x8003, 146},
+        {0x8006, 146},
+        {0x800a, 146},
+        {0x800f, 146},
+        {0x8018, 146},
+        {0x801f, 146},
+        {0x8029, 146},
+        {0xc038, 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},
+        {0x8001, 154},
+        {0xc016, 154},
+        {0x8001, 156},
+        {0xc016, 156},
+        {0x8001, 160},
+        {0xc016, 160},
+        {0x8001, 163},
+        {0xc016, 163},
+        {0x8001, 164},
+        {0xc016, 164},
+        {0x8001, 169},
+        {0xc016, 169},
+        {0x8001, 170},
+        {0xc016, 170},
+        {0x8001, 173},
+        {0xc016, 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},
+        {0x8002, 154},
+        {0x8009, 154},
+        {0x8017, 154},
+        {0xc028, 154},
+        {0x8002, 156},
+        {0x8009, 156},
+        {0x8017, 156},
+        {0xc028, 156},
+        {0x8002, 160},
+        {0x8009, 160},
+        {0x8017, 160},
+        {0xc028, 160},
+        {0x8002, 163},
+        {0x8009, 163},
+        {0x8017, 163},
+        {0xc028, 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},
+        {0x8003, 154},
+        {0x8006, 154},
+        {0x800a, 154},
+        {0x800f, 154},
+        {0x8018, 154},
+        {0x801f, 154},
+        {0x8029, 154},
+        {0xc038, 154},
+        {0x8003, 156},
+        {0x8006, 156},
+        {0x800a, 156},
+        {0x800f, 156},
+        {0x8018, 156},
+        {0x801f, 156},
+        {0x8029, 156},
+        {0xc038, 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},
+        {0x8003, 160},
+        {0x8006, 160},
+        {0x800a, 160},
+        {0x800f, 160},
+        {0x8018, 160},
+        {0x801f, 160},
+        {0x8029, 160},
+        {0xc038, 160},
+        {0x8003, 163},
+        {0x8006, 163},
+        {0x800a, 163},
+        {0x800f, 163},
+        {0x8018, 163},
+        {0x801f, 163},
+        {0x8029, 163},
+        {0xc038, 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},
+        {0x8002, 164},
+        {0x8009, 164},
+        {0x8017, 164},
+        {0xc028, 164},
+        {0x8002, 169},
+        {0x8009, 169},
+        {0x8017, 169},
+        {0xc028, 169},
+        {0x8002, 170},
+        {0x8009, 170},
+        {0x8017, 170},
+        {0xc028, 170},
+        {0x8002, 173},
+        {0x8009, 173},
+        {0x8017, 173},
+        {0xc028, 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},
+        {0x8003, 164},
+        {0x8006, 164},
+        {0x800a, 164},
+        {0x800f, 164},
+        {0x8018, 164},
+        {0x801f, 164},
+        {0x8029, 164},
+        {0xc038, 164},
+        {0x8003, 169},
+        {0x8006, 169},
+        {0x800a, 169},
+        {0x800f, 169},
+        {0x8018, 169},
+        {0x801f, 169},
+        {0x8029, 169},
+        {0xc038, 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},
+        {0x8003, 170},
+        {0x8006, 170},
+        {0x800a, 170},
+        {0x800f, 170},
+        {0x8018, 170},
+        {0x801f, 170},
+        {0x8029, 170},
+        {0xc038, 170},
+        {0x8003, 173},
+        {0x8006, 173},
+        {0x800a, 173},
+        {0x800f, 173},
+        {0x8018, 173},
+        {0x801f, 173},
+        {0x8029, 173},
+        {0xc038, 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},
+        {0x89, 0},
+        {0x8a, 0},
+        {0x8c, 0},
+        {0x8d, 0},
+        {0x90, 0},
+        {0x91, 0},
+        {0x93, 0},
+        {0x96, 0},
+        {0x9c, 0},
+        {0x9f, 0},
+        {0xa3, 0},
+        {0xa6, 0},
+        {0xab, 0},
+        {0xae, 0},
+        {0xb5, 0},
+        {0xbe, 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},
+        {0xc000, 178},
+        {0xc000, 181},
+        {0xc000, 185},
+        {0xc000, 186},
+        {0xc000, 187},
+        {0xc000, 189},
+        {0xc000, 190},
+        {0xc000, 196},
+        {0xc000, 198},
+        {0xc000, 228},
+        {0xc000, 232},
+        {0xc000, 233},
+        {0x94, 0},
+        {0x95, 0},
+        {0x97, 0},
+        {0x98, 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},
+        {0x8001, 178},
+        {0xc016, 178},
+        {0x8001, 181},
+        {0xc016, 181},
+        {0x8001, 185},
+        {0xc016, 185},
+        {0x8001, 186},
+        {0xc016, 186},
+        {0x8001, 187},
+        {0xc016, 187},
+        {0x8001, 189},
+        {0xc016, 189},
+        {0x8001, 190},
+        {0xc016, 190},
+        {0x8001, 196},
+        {0xc016, 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},
+        {0x8002, 178},
+        {0x8009, 178},
+        {0x8017, 178},
+        {0xc028, 178},
+        {0x8002, 181},
+        {0x8009, 181},
+        {0x8017, 181},
+        {0xc028, 181},
+        {0x8002, 185},
+        {0x8009, 185},
+        {0x8017, 185},
+        {0xc028, 185},
+        {0x8002, 186},
+        {0x8009, 186},
+        {0x8017, 186},
+        {0xc028, 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},
+        {0x8003, 178},
+        {0x8006, 178},
+        {0x800a, 178},
+        {0x800f, 178},
+        {0x8018, 178},
+        {0x801f, 178},
+        {0x8029, 178},
+        {0xc038, 178},
+        {0x8003, 181},
+        {0x8006, 181},
+        {0x800a, 181},
+        {0x800f, 181},
+        {0x8018, 181},
+        {0x801f, 181},
+        {0x8029, 181},
+        {0xc038, 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},
+        {0x8003, 185},
+        {0x8006, 185},
+        {0x800a, 185},
+        {0x800f, 185},
+        {0x8018, 185},
+        {0x801f, 185},
+        {0x8029, 185},
+        {0xc038, 185},
+        {0x8003, 186},
+        {0x8006, 186},
+        {0x800a, 186},
+        {0x800f, 186},
+        {0x8018, 186},
+        {0x801f, 186},
+        {0x8029, 186},
+        {0xc038, 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},
+        {0x8002, 187},
+        {0x8009, 187},
+        {0x8017, 187},
+        {0xc028, 187},
+        {0x8002, 189},
+        {0x8009, 189},
+        {0x8017, 189},
+        {0xc028, 189},
+        {0x8002, 190},
+        {0x8009, 190},
+        {0x8017, 190},
+        {0xc028, 190},
+        {0x8002, 196},
+        {0x8009, 196},
+        {0x8017, 196},
+        {0xc028, 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},
+        {0x8003, 187},
+        {0x8006, 187},
+        {0x800a, 187},
+        {0x800f, 187},
+        {0x8018, 187},
+        {0x801f, 187},
+        {0x8029, 187},
+        {0xc038, 187},
+        {0x8003, 189},
+        {0x8006, 189},
+        {0x800a, 189},
+        {0x800f, 189},
+        {0x8018, 189},
+        {0x801f, 189},
+        {0x8029, 189},
+        {0xc038, 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},
+        {0x8003, 190},
+        {0x8006, 190},
+        {0x800a, 190},
+        {0x800f, 190},
+        {0x8018, 190},
+        {0x801f, 190},
+        {0x8029, 190},
+        {0xc038, 190},
+        {0x8003, 196},
+        {0x8006, 196},
+        {0x800a, 196},
+        {0x800f, 196},
+        {0x8018, 196},
+        {0x801f, 196},
+        {0x8029, 196},
+        {0xc038, 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},
+        {0x8001, 198},
+        {0xc016, 198},
+        {0x8001, 228},
+        {0xc016, 228},
+        {0x8001, 232},
+        {0xc016, 232},
+        {0x8001, 233},
+        {0xc016, 233},
+        {0xc000, 1},
+        {0xc000, 135},
+        {0xc000, 137},
+        {0xc000, 138},
+        {0xc000, 139},
+        {0xc000, 140},
+        {0xc000, 141},
+        {0xc000, 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},
+        {0x8002, 198},
+        {0x8009, 198},
+        {0x8017, 198},
+        {0xc028, 198},
+        {0x8002, 228},
+        {0x8009, 228},
+        {0x8017, 228},
+        {0xc028, 228},
+        {0x8002, 232},
+        {0x8009, 232},
+        {0x8017, 232},
+        {0xc028, 232},
+        {0x8002, 233},
+        {0x8009, 233},
+        {0x8017, 233},
+        {0xc028, 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},
+        {0x8003, 198},
+        {0x8006, 198},
+        {0x800a, 198},
+        {0x800f, 198},
+        {0x8018, 198},
+        {0x801f, 198},
+        {0x8029, 198},
+        {0xc038, 198},
+        {0x8003, 228},
+        {0x8006, 228},
+        {0x800a, 228},
+        {0x800f, 228},
+        {0x8018, 228},
+        {0x801f, 228},
+        {0x8029, 228},
+        {0xc038, 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},
+        {0x8003, 232},
+        {0x8006, 232},
+        {0x800a, 232},
+        {0x800f, 232},
+        {0x8018, 232},
+        {0x801f, 232},
+        {0x8029, 232},
+        {0xc038, 232},
+        {0x8003, 233},
+        {0x8006, 233},
+        {0x800a, 233},
+        {0x800f, 233},
+        {0x8018, 233},
+        {0x801f, 233},
+        {0x8029, 233},
+        {0xc038, 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},
+        {0x8001, 1},
+        {0xc016, 1},
+        {0x8001, 135},
+        {0xc016, 135},
+        {0x8001, 137},
+        {0xc016, 137},
+        {0x8001, 138},
+        {0xc016, 138},
+        {0x8001, 139},
+        {0xc016, 139},
+        {0x8001, 140},
+        {0xc016, 140},
+        {0x8001, 141},
+        {0xc016, 141},
+        {0x8001, 143},
+        {0xc016, 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},
+        {0x8002, 1},
+        {0x8009, 1},
+        {0x8017, 1},
+        {0xc028, 1},
+        {0x8002, 135},
+        {0x8009, 135},
+        {0x8017, 135},
+        {0xc028, 135},
+        {0x8002, 137},
+        {0x8009, 137},
+        {0x8017, 137},
+        {0xc028, 137},
+        {0x8002, 138},
+        {0x8009, 138},
+        {0x8017, 138},
+        {0xc028, 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},
+        {0x8003, 1},
+        {0x8006, 1},
+        {0x800a, 1},
+        {0x800f, 1},
+        {0x8018, 1},
+        {0x801f, 1},
+        {0x8029, 1},
+        {0xc038, 1},
+        {0x8003, 135},
+        {0x8006, 135},
+        {0x800a, 135},
+        {0x800f, 135},
+        {0x8018, 135},
+        {0x801f, 135},
+        {0x8029, 135},
+        {0xc038, 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},
+        {0x8003, 137},
+        {0x8006, 137},
+        {0x800a, 137},
+        {0x800f, 137},
+        {0x8018, 137},
+        {0x801f, 137},
+        {0x8029, 137},
+        {0xc038, 137},
+        {0x8003, 138},
+        {0x8006, 138},
+        {0x800a, 138},
+        {0x800f, 138},
+        {0x8018, 138},
+        {0x801f, 138},
+        {0x8029, 138},
+        {0xc038, 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},
+        {0x8002, 139},
+        {0x8009, 139},
+        {0x8017, 139},
+        {0xc028, 139},
+        {0x8002, 140},
+        {0x8009, 140},
+        {0x8017, 140},
+        {0xc028, 140},
+        {0x8002, 141},
+        {0x8009, 141},
+        {0x8017, 141},
+        {0xc028, 141},
+        {0x8002, 143},
+        {0x8009, 143},
+        {0x8017, 143},
+        {0xc028, 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},
+        {0x8003, 139},
+        {0x8006, 139},
+        {0x800a, 139},
+        {0x800f, 139},
+        {0x8018, 139},
+        {0x801f, 139},
+        {0x8029, 139},
+        {0xc038, 139},
+        {0x8003, 140},
+        {0x8006, 140},
+        {0x800a, 140},
+        {0x800f, 140},
+        {0x8018, 140},
+        {0x801f, 140},
+        {0x8029, 140},
+        {0xc038, 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},
+        {0x8003, 141},
+        {0x8006, 141},
+        {0x800a, 141},
+        {0x800f, 141},
+        {0x8018, 141},
+        {0x801f, 141},
+        {0x8029, 141},
+        {0xc038, 141},
+        {0x8003, 143},
+        {0x8006, 143},
+        {0x800a, 143},
+        {0x800f, 143},
+        {0x8018, 143},
+        {0x801f, 143},
+        {0x8029, 143},
+        {0xc038, 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},
+        {0x9d, 0},
+        {0x9e, 0},
+        {0xa0, 0},
+        {0xa1, 0},
+        {0xa4, 0},
+        {0xa5, 0},
+        {0xa7, 0},
+        {0xa8, 0},
+        {0xac, 0},
+        {0xad, 0},
+        {0xaf, 0},
+        {0xb1, 0},
+        {0xb6, 0},
+        {0xb9, 0},
+        {0xbf, 0},
+        {0xcf, 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},
+        {0xc000, 147},
+        {0xc000, 149},
+        {0xc000, 150},
+        {0xc000, 151},
+        {0xc000, 152},
+        {0xc000, 155},
+        {0xc000, 157},
+        {0xc000, 158},
+        {0xc000, 165},
+        {0xc000, 166},
+        {0xc000, 168},
+        {0xc000, 174},
+        {0xc000, 175},
+        {0xc000, 180},
+        {0xc000, 182},
+        {0xc000, 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},
+        {0x8001, 147},
+        {0xc016, 147},
+        {0x8001, 149},
+        {0xc016, 149},
+        {0x8001, 150},
+        {0xc016, 150},
+        {0x8001, 151},
+        {0xc016, 151},
+        {0x8001, 152},
+        {0xc016, 152},
+        {0x8001, 155},
+        {0xc016, 155},
+        {0x8001, 157},
+        {0xc016, 157},
+        {0x8001, 158},
+        {0xc016, 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},
+        {0x8002, 147},
+        {0x8009, 147},
+        {0x8017, 147},
+        {0xc028, 147},
+        {0x8002, 149},
+        {0x8009, 149},
+        {0x8017, 149},
+        {0xc028, 149},
+        {0x8002, 150},
+        {0x8009, 150},
+        {0x8017, 150},
+        {0xc028, 150},
+        {0x8002, 151},
+        {0x8009, 151},
+        {0x8017, 151},
+        {0xc028, 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},
+        {0x8003, 147},
+        {0x8006, 147},
+        {0x800a, 147},
+        {0x800f, 147},
+        {0x8018, 147},
+        {0x801f, 147},
+        {0x8029, 147},
+        {0xc038, 147},
+        {0x8003, 149},
+        {0x8006, 149},
+        {0x800a, 149},
+        {0x800f, 149},
+        {0x8018, 149},
+        {0x801f, 149},
+        {0x8029, 149},
+        {0xc038, 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},
+        {0x8003, 150},
+        {0x8006, 150},
+        {0x800a, 150},
+        {0x800f, 150},
+        {0x8018, 150},
+        {0x801f, 150},
+        {0x8029, 150},
+        {0xc038, 150},
+        {0x8003, 151},
+        {0x8006, 151},
+        {0x800a, 151},
+        {0x800f, 151},
+        {0x8018, 151},
+        {0x801f, 151},
+        {0x8029, 151},
+        {0xc038, 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},
+        {0x8002, 152},
+        {0x8009, 152},
+        {0x8017, 152},
+        {0xc028, 152},
+        {0x8002, 155},
+        {0x8009, 155},
+        {0x8017, 155},
+        {0xc028, 155},
+        {0x8002, 157},
+        {0x8009, 157},
+        {0x8017, 157},
+        {0xc028, 157},
+        {0x8002, 158},
+        {0x8009, 158},
+        {0x8017, 158},
+        {0xc028, 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},
+        {0x8003, 152},
+        {0x8006, 152},
+        {0x800a, 152},
+        {0x800f, 152},
+        {0x8018, 152},
+        {0x801f, 152},
+        {0x8029, 152},
+        {0xc038, 152},
+        {0x8003, 155},
+        {0x8006, 155},
+        {0x800a, 155},
+        {0x800f, 155},
+        {0x8018, 155},
+        {0x801f, 155},
+        {0x8029, 155},
+        {0xc038, 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},
+        {0x8003, 157},
+        {0x8006, 157},
+        {0x800a, 157},
+        {0x800f, 157},
+        {0x8018, 157},
+        {0x801f, 157},
+        {0x8029, 157},
+        {0xc038, 157},
+        {0x8003, 158},
+        {0x8006, 158},
+        {0x800a, 158},
+        {0x800f, 158},
+        {0x8018, 158},
+        {0x801f, 158},
+        {0x8029, 158},
+        {0xc038, 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},
+        {0x8001, 165},
+        {0xc016, 165},
+        {0x8001, 166},
+        {0xc016, 166},
+        {0x8001, 168},
+        {0xc016, 168},
+        {0x8001, 174},
+        {0xc016, 174},
+        {0x8001, 175},
+        {0xc016, 175},
+        {0x8001, 180},
+        {0xc016, 180},
+        {0x8001, 182},
+        {0xc016, 182},
+        {0x8001, 183},
+        {0xc016, 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},
+        {0x8002, 165},
+        {0x8009, 165},
+        {0x8017, 165},
+        {0xc028, 165},
+        {0x8002, 166},
+        {0x8009, 166},
+        {0x8017, 166},
+        {0xc028, 166},
+        {0x8002, 168},
+        {0x8009, 168},
+        {0x8017, 168},
+        {0xc028, 168},
+        {0x8002, 174},
+        {0x8009, 174},
+        {0x8017, 174},
+        {0xc028, 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},
+        {0x8003, 165},
+        {0x8006, 165},
+        {0x800a, 165},
+        {0x800f, 165},
+        {0x8018, 165},
+        {0x801f, 165},
+        {0x8029, 165},
+        {0xc038, 165},
+        {0x8003, 166},
+        {0x8006, 166},
+        {0x800a, 166},
+        {0x800f, 166},
+        {0x8018, 166},
+        {0x801f, 166},
+        {0x8029, 166},
+        {0xc038, 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},
+        {0x8003, 168},
+        {0x8006, 168},
+        {0x800a, 168},
+        {0x800f, 168},
+        {0x8018, 168},
+        {0x801f, 168},
+        {0x8029, 168},
+        {0xc038, 168},
+        {0x8003, 174},
+        {0x8006, 174},
+        {0x800a, 174},
+        {0x800f, 174},
+        {0x8018, 174},
+        {0x801f, 174},
+        {0x8029, 174},
+        {0xc038, 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},
+        {0x8002, 175},
+        {0x8009, 175},
+        {0x8017, 175},
+        {0xc028, 175},
+        {0x8002, 180},
+        {0x8009, 180},
+        {0x8017, 180},
+        {0xc028, 180},
+        {0x8002, 182},
+        {0x8009, 182},
+        {0x8017, 182},
+        {0xc028, 182},
+        {0x8002, 183},
+        {0x8009, 183},
+        {0x8017, 183},
+        {0xc028, 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},
+        {0x8003, 175},
+        {0x8006, 175},
+        {0x800a, 175},
+        {0x800f, 175},
+        {0x8018, 175},
+        {0x801f, 175},
+        {0x8029, 175},
+        {0xc038, 175},
+        {0x8003, 180},
+        {0x8006, 180},
+        {0x800a, 180},
+        {0x800f, 180},
+        {0x8018, 180},
+        {0x801f, 180},
+        {0x8029, 180},
+        {0xc038, 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},
+        {0x8003, 182},
+        {0x8006, 182},
+        {0x800a, 182},
+        {0x800f, 182},
+        {0x8018, 182},
+        {0x801f, 182},
+        {0x8029, 182},
+        {0xc038, 182},
+        {0x8003, 183},
+        {0x8006, 183},
+        {0x800a, 183},
+        {0x800f, 183},
+        {0x8018, 183},
+        {0x801f, 183},
+        {0x8029, 183},
+        {0xc038, 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},
+        {0xc000, 188},
+        {0xc000, 191},
+        {0xc000, 197},
+        {0xc000, 231},
+        {0xc000, 239},
+        {0xb0, 0},
+        {0xb2, 0},
+        {0xb3, 0},
+        {0xb7, 0},
+        {0xb8, 0},
+        {0xba, 0},
+        {0xbb, 0},
+        {0xc0, 0},
+        {0xc7, 0},
+        {0xd0, 0},
+        {0xdf, 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},
+        {0x8001, 188},
+        {0xc016, 188},
+        {0x8001, 191},
+        {0xc016, 191},
+        {0x8001, 197},
+        {0xc016, 197},
+        {0x8001, 231},
+        {0xc016, 231},
+        {0x8001, 239},
+        {0xc016, 239},
+        {0xc000, 9},
+        {0xc000, 142},
+        {0xc000, 144},
+        {0xc000, 145},
+        {0xc000, 148},
+        {0xc000, 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},
+        {0x8002, 188},
+        {0x8009, 188},
+        {0x8017, 188},
+        {0xc028, 188},
+        {0x8002, 191},
+        {0x8009, 191},
+        {0x8017, 191},
+        {0xc028, 191},
+        {0x8002, 197},
+        {0x8009, 197},
+        {0x8017, 197},
+        {0xc028, 197},
+        {0x8002, 231},
+        {0x8009, 231},
+        {0x8017, 231},
+        {0xc028, 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},
+        {0x8003, 188},
+        {0x8006, 188},
+        {0x800a, 188},
+        {0x800f, 188},
+        {0x8018, 188},
+        {0x801f, 188},
+        {0x8029, 188},
+        {0xc038, 188},
+        {0x8003, 191},
+        {0x8006, 191},
+        {0x800a, 191},
+        {0x800f, 191},
+        {0x8018, 191},
+        {0x801f, 191},
+        {0x8029, 191},
+        {0xc038, 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},
+        {0x8003, 197},
+        {0x8006, 197},
+        {0x800a, 197},
+        {0x800f, 197},
+        {0x8018, 197},
+        {0x801f, 197},
+        {0x8029, 197},
+        {0xc038, 197},
+        {0x8003, 231},
+        {0x8006, 231},
+        {0x800a, 231},
+        {0x800f, 231},
+        {0x8018, 231},
+        {0x801f, 231},
+        {0x8029, 231},
+        {0xc038, 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},
+        {0x8002, 239},
+        {0x8009, 239},
+        {0x8017, 239},
+        {0xc028, 239},
+        {0x8001, 9},
+        {0xc016, 9},
+        {0x8001, 142},
+        {0xc016, 142},
+        {0x8001, 144},
+        {0xc016, 144},
+        {0x8001, 145},
+        {0xc016, 145},
+        {0x8001, 148},
+        {0xc016, 148},
+        {0x8001, 159},
+        {0xc016, 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},
+        {0x8003, 239},
+        {0x8006, 239},
+        {0x800a, 239},
+        {0x800f, 239},
+        {0x8018, 239},
+        {0x801f, 239},
+        {0x8029, 239},
+        {0xc038, 239},
+        {0x8002, 9},
+        {0x8009, 9},
+        {0x8017, 9},
+        {0xc028, 9},
+        {0x8002, 142},
+        {0x8009, 142},
+        {0x8017, 142},
+        {0xc028, 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},
+        {0x8003, 9},
+        {0x8006, 9},
+        {0x800a, 9},
+        {0x800f, 9},
+        {0x8018, 9},
+        {0x801f, 9},
+        {0x8029, 9},
+        {0xc038, 9},
+        {0x8003, 142},
+        {0x8006, 142},
+        {0x800a, 142},
+        {0x800f, 142},
+        {0x8018, 142},
+        {0x801f, 142},
+        {0x8029, 142},
+        {0xc038, 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},
+        {0x8002, 144},
+        {0x8009, 144},
+        {0x8017, 144},
+        {0xc028, 144},
+        {0x8002, 145},
+        {0x8009, 145},
+        {0x8017, 145},
+        {0xc028, 145},
+        {0x8002, 148},
+        {0x8009, 148},
+        {0x8017, 148},
+        {0xc028, 148},
+        {0x8002, 159},
+        {0x8009, 159},
+        {0x8017, 159},
+        {0xc028, 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},
+        {0x8003, 144},
+        {0x8006, 144},
+        {0x800a, 144},
+        {0x800f, 144},
+        {0x8018, 144},
+        {0x801f, 144},
+        {0x8029, 144},
+        {0xc038, 144},
+        {0x8003, 145},
+        {0x8006, 145},
+        {0x800a, 145},
+        {0x800f, 145},
+        {0x8018, 145},
+        {0x801f, 145},
+        {0x8029, 145},
+        {0xc038, 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},
+        {0x8003, 148},
+        {0x8006, 148},
+        {0x800a, 148},
+        {0x800f, 148},
+        {0x8018, 148},
+        {0x801f, 148},
+        {0x8029, 148},
+        {0xc038, 148},
+        {0x8003, 159},
+        {0x8006, 159},
+        {0x800a, 159},
+        {0x800f, 159},
+        {0x8018, 159},
+        {0x801f, 159},
+        {0x8029, 159},
+        {0xc038, 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},
+        {0xc000, 171},
+        {0xc000, 206},
+        {0xc000, 215},
+        {0xc000, 225},
+        {0xc000, 236},
+        {0xc000, 237},
+        {0xbc, 0},
+        {0xbd, 0},
+        {0xc1, 0},
+        {0xc4, 0},
+        {0xc8, 0},
+        {0xcb, 0},
+        {0xd1, 0},
+        {0xd8, 0},
+        {0xe0, 0},
+        {0xee, 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},
+        {0x8001, 171},
+        {0xc016, 171},
+        {0x8001, 206},
+        {0xc016, 206},
+        {0x8001, 215},
+        {0xc016, 215},
+        {0x8001, 225},
+        {0xc016, 225},
+        {0x8001, 236},
+        {0xc016, 236},
+        {0x8001, 237},
+        {0xc016, 237},
+        {0xc000, 199},
+        {0xc000, 207},
+        {0xc000, 234},
+        {0xc000, 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},
+        {0x8002, 171},
+        {0x8009, 171},
+        {0x8017, 171},
+        {0xc028, 171},
+        {0x8002, 206},
+        {0x8009, 206},
+        {0x8017, 206},
+        {0xc028, 206},
+        {0x8002, 215},
+        {0x8009, 215},
+        {0x8017, 215},
+        {0xc028, 215},
+        {0x8002, 225},
+        {0x8009, 225},
+        {0x8017, 225},
+        {0xc028, 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},
+        {0x8003, 171},
+        {0x8006, 171},
+        {0x800a, 171},
+        {0x800f, 171},
+        {0x8018, 171},
+        {0x801f, 171},
+        {0x8029, 171},
+        {0xc038, 171},
+        {0x8003, 206},
+        {0x8006, 206},
+        {0x800a, 206},
+        {0x800f, 206},
+        {0x8018, 206},
+        {0x801f, 206},
+        {0x8029, 206},
+        {0xc038, 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},
+        {0x8003, 215},
+        {0x8006, 215},
+        {0x800a, 215},
+        {0x800f, 215},
+        {0x8018, 215},
+        {0x801f, 215},
+        {0x8029, 215},
+        {0xc038, 215},
+        {0x8003, 225},
+        {0x8006, 225},
+        {0x800a, 225},
+        {0x800f, 225},
+        {0x8018, 225},
+        {0x801f, 225},
+        {0x8029, 225},
+        {0xc038, 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},
+        {0x8002, 236},
+        {0x8009, 236},
+        {0x8017, 236},
+        {0xc028, 236},
+        {0x8002, 237},
+        {0x8009, 237},
+        {0x8017, 237},
+        {0xc028, 237},
+        {0x8001, 199},
+        {0xc016, 199},
+        {0x8001, 207},
+        {0xc016, 207},
+        {0x8001, 234},
+        {0xc016, 234},
+        {0x8001, 235},
+        {0xc016, 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},
+        {0x8003, 236},
+        {0x8006, 236},
+        {0x800a, 236},
+        {0x800f, 236},
+        {0x8018, 236},
+        {0x801f, 236},
+        {0x8029, 236},
+        {0xc038, 236},
+        {0x8003, 237},
+        {0x8006, 237},
+        {0x800a, 237},
+        {0x800f, 237},
+        {0x8018, 237},
+        {0x801f, 237},
+        {0x8029, 237},
+        {0xc038, 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},
+        {0x8002, 199},
+        {0x8009, 199},
+        {0x8017, 199},
+        {0xc028, 199},
+        {0x8002, 207},
+        {0x8009, 207},
+        {0x8017, 207},
+        {0xc028, 207},
+        {0x8002, 234},
+        {0x8009, 234},
+        {0x8017, 234},
+        {0xc028, 234},
+        {0x8002, 235},
+        {0x8009, 235},
+        {0x8017, 235},
+        {0xc028, 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},
+        {0x8003, 199},
+        {0x8006, 199},
+        {0x800a, 199},
+        {0x800f, 199},
+        {0x8018, 199},
+        {0x801f, 199},
+        {0x8029, 199},
+        {0xc038, 199},
+        {0x8003, 207},
+        {0x8006, 207},
+        {0x800a, 207},
+        {0x800f, 207},
+        {0x8018, 207},
+        {0x801f, 207},
+        {0x8029, 207},
+        {0xc038, 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},
+        {0x8003, 234},
+        {0x8006, 234},
+        {0x800a, 234},
+        {0x800f, 234},
+        {0x8018, 234},
+        {0x801f, 234},
+        {0x8029, 234},
+        {0xc038, 234},
+        {0x8003, 235},
+        {0x8006, 235},
+        {0x800a, 235},
+        {0x800f, 235},
+        {0x8018, 235},
+        {0x801f, 235},
+        {0x8029, 235},
+        {0xc038, 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},
+        {0xc2, 0},
+        {0xc3, 0},
+        {0xc5, 0},
+        {0xc6, 0},
+        {0xc9, 0},
+        {0xca, 0},
+        {0xcc, 0},
+        {0xcd, 0},
+        {0xd2, 0},
+        {0xd5, 0},
+        {0xd9, 0},
+        {0xdc, 0},
+        {0xe1, 0},
+        {0xe7, 0},
+        {0xef, 0},
+        {0xf6, 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},
+        {0xc000, 192},
+        {0xc000, 193},
+        {0xc000, 200},
+        {0xc000, 201},
+        {0xc000, 202},
+        {0xc000, 205},
+        {0xc000, 210},
+        {0xc000, 213},
+        {0xc000, 218},
+        {0xc000, 219},
+        {0xc000, 238},
+        {0xc000, 240},
+        {0xc000, 242},
+        {0xc000, 243},
+        {0xc000, 255},
+        {0xce, 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},
+        {0x8001, 192},
+        {0xc016, 192},
+        {0x8001, 193},
+        {0xc016, 193},
+        {0x8001, 200},
+        {0xc016, 200},
+        {0x8001, 201},
+        {0xc016, 201},
+        {0x8001, 202},
+        {0xc016, 202},
+        {0x8001, 205},
+        {0xc016, 205},
+        {0x8001, 210},
+        {0xc016, 210},
+        {0x8001, 213},
+        {0xc016, 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},
+        {0x8002, 192},
+        {0x8009, 192},
+        {0x8017, 192},
+        {0xc028, 192},
+        {0x8002, 193},
+        {0x8009, 193},
+        {0x8017, 193},
+        {0xc028, 193},
+        {0x8002, 200},
+        {0x8009, 200},
+        {0x8017, 200},
+        {0xc028, 200},
+        {0x8002, 201},
+        {0x8009, 201},
+        {0x8017, 201},
+        {0xc028, 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},
+        {0x8003, 192},
+        {0x8006, 192},
+        {0x800a, 192},
+        {0x800f, 192},
+        {0x8018, 192},
+        {0x801f, 192},
+        {0x8029, 192},
+        {0xc038, 192},
+        {0x8003, 193},
+        {0x8006, 193},
+        {0x800a, 193},
+        {0x800f, 193},
+        {0x8018, 193},
+        {0x801f, 193},
+        {0x8029, 193},
+        {0xc038, 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},
+        {0x8003, 200},
+        {0x8006, 200},
+        {0x800a, 200},
+        {0x800f, 200},
+        {0x8018, 200},
+        {0x801f, 200},
+        {0x8029, 200},
+        {0xc038, 200},
+        {0x8003, 201},
+        {0x8006, 201},
+        {0x800a, 201},
+        {0x800f, 201},
+        {0x8018, 201},
+        {0x801f, 201},
+        {0x8029, 201},
+        {0xc038, 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},
+        {0x8002, 202},
+        {0x8009, 202},
+        {0x8017, 202},
+        {0xc028, 202},
+        {0x8002, 205},
+        {0x8009, 205},
+        {0x8017, 205},
+        {0xc028, 205},
+        {0x8002, 210},
+        {0x8009, 210},
+        {0x8017, 210},
+        {0xc028, 210},
+        {0x8002, 213},
+        {0x8009, 213},
+        {0x8017, 213},
+        {0xc028, 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},
+        {0x8003, 202},
+        {0x8006, 202},
+        {0x800a, 202},
+        {0x800f, 202},
+        {0x8018, 202},
+        {0x801f, 202},
+        {0x8029, 202},
+        {0xc038, 202},
+        {0x8003, 205},
+        {0x8006, 205},
+        {0x800a, 205},
+        {0x800f, 205},
+        {0x8018, 205},
+        {0x801f, 205},
+        {0x8029, 205},
+        {0xc038, 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},
+        {0x8003, 210},
+        {0x8006, 210},
+        {0x800a, 210},
+        {0x800f, 210},
+        {0x8018, 210},
+        {0x801f, 210},
+        {0x8029, 210},
+        {0xc038, 210},
+        {0x8003, 213},
+        {0x8006, 213},
+        {0x800a, 213},
+        {0x800f, 213},
+        {0x8018, 213},
+        {0x801f, 213},
+        {0x8029, 213},
+        {0xc038, 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},
+        {0x8001, 218},
+        {0xc016, 218},
+        {0x8001, 219},
+        {0xc016, 219},
+        {0x8001, 238},
+        {0xc016, 238},
+        {0x8001, 240},
+        {0xc016, 240},
+        {0x8001, 242},
+        {0xc016, 242},
+        {0x8001, 243},
+        {0xc016, 243},
+        {0x8001, 255},
+        {0xc016, 255},
+        {0xc000, 203},
+        {0xc000, 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},
+        {0x8002, 218},
+        {0x8009, 218},
+        {0x8017, 218},
+        {0xc028, 218},
+        {0x8002, 219},
+        {0x8009, 219},
+        {0x8017, 219},
+        {0xc028, 219},
+        {0x8002, 238},
+        {0x8009, 238},
+        {0x8017, 238},
+        {0xc028, 238},
+        {0x8002, 240},
+        {0x8009, 240},
+        {0x8017, 240},
+        {0xc028, 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},
+        {0x8003, 218},
+        {0x8006, 218},
+        {0x800a, 218},
+        {0x800f, 218},
+        {0x8018, 218},
+        {0x801f, 218},
+        {0x8029, 218},
+        {0xc038, 218},
+        {0x8003, 219},
+        {0x8006, 219},
+        {0x800a, 219},
+        {0x800f, 219},
+        {0x8018, 219},
+        {0x801f, 219},
+        {0x8029, 219},
+        {0xc038, 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},
+        {0x8003, 238},
+        {0x8006, 238},
+        {0x800a, 238},
+        {0x800f, 238},
+        {0x8018, 238},
+        {0x801f, 238},
+        {0x8029, 238},
+        {0xc038, 238},
+        {0x8003, 240},
+        {0x8006, 240},
+        {0x800a, 240},
+        {0x800f, 240},
+        {0x8018, 240},
+        {0x801f, 240},
+        {0x8029, 240},
+        {0xc038, 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},
+        {0x8002, 242},
+        {0x8009, 242},
+        {0x8017, 242},
+        {0xc028, 242},
+        {0x8002, 243},
+        {0x8009, 243},
+        {0x8017, 243},
+        {0xc028, 243},
+        {0x8002, 255},
+        {0x8009, 255},
+        {0x8017, 255},
+        {0xc028, 255},
+        {0x8001, 203},
+        {0xc016, 203},
+        {0x8001, 204},
+        {0xc016, 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},
+        {0x8003, 242},
+        {0x8006, 242},
+        {0x800a, 242},
+        {0x800f, 242},
+        {0x8018, 242},
+        {0x801f, 242},
+        {0x8029, 242},
+        {0xc038, 242},
+        {0x8003, 243},
+        {0x8006, 243},
+        {0x800a, 243},
+        {0x800f, 243},
+        {0x8018, 243},
+        {0x801f, 243},
+        {0x8029, 243},
+        {0xc038, 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},
+        {0x8003, 255},
+        {0x8006, 255},
+        {0x800a, 255},
+        {0x800f, 255},
+        {0x8018, 255},
+        {0x801f, 255},
+        {0x8029, 255},
+        {0xc038, 255},
+        {0x8002, 203},
+        {0x8009, 203},
+        {0x8017, 203},
+        {0xc028, 203},
+        {0x8002, 204},
+        {0x8009, 204},
+        {0x8017, 204},
+        {0xc028, 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},
+        {0x8003, 203},
+        {0x8006, 203},
+        {0x800a, 203},
+        {0x800f, 203},
+        {0x8018, 203},
+        {0x801f, 203},
+        {0x8029, 203},
+        {0xc038, 203},
+        {0x8003, 204},
+        {0x8006, 204},
+        {0x800a, 204},
+        {0x800f, 204},
+        {0x8018, 204},
+        {0x801f, 204},
+        {0x8029, 204},
+        {0xc038, 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},
+        {0xd3, 0},
+        {0xd4, 0},
+        {0xd6, 0},
+        {0xd7, 0},
+        {0xda, 0},
+        {0xdb, 0},
+        {0xdd, 0},
+        {0xde, 0},
+        {0xe2, 0},
+        {0xe4, 0},
+        {0xe8, 0},
+        {0xeb, 0},
+        {0xf0, 0},
+        {0xf3, 0},
+        {0xf7, 0},
+        {0xfa, 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},
+        {0xc000, 211},
+        {0xc000, 212},
+        {0xc000, 214},
+        {0xc000, 221},
+        {0xc000, 222},
+        {0xc000, 223},
+        {0xc000, 241},
+        {0xc000, 244},
+        {0xc000, 245},
+        {0xc000, 246},
+        {0xc000, 247},
+        {0xc000, 248},
+        {0xc000, 250},
+        {0xc000, 251},
+        {0xc000, 252},
+        {0xc000, 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},
+        {0x8001, 211},
+        {0xc016, 211},
+        {0x8001, 212},
+        {0xc016, 212},
+        {0x8001, 214},
+        {0xc016, 214},
+        {0x8001, 221},
+        {0xc016, 221},
+        {0x8001, 222},
+        {0xc016, 222},
+        {0x8001, 223},
+        {0xc016, 223},
+        {0x8001, 241},
+        {0xc016, 241},
+        {0x8001, 244},
+        {0xc016, 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},
+        {0x8002, 211},
+        {0x8009, 211},
+        {0x8017, 211},
+        {0xc028, 211},
+        {0x8002, 212},
+        {0x8009, 212},
+        {0x8017, 212},
+        {0xc028, 212},
+        {0x8002, 214},
+        {0x8009, 214},
+        {0x8017, 214},
+        {0xc028, 214},
+        {0x8002, 221},
+        {0x8009, 221},
+        {0x8017, 221},
+        {0xc028, 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},
+        {0x8003, 211},
+        {0x8006, 211},
+        {0x800a, 211},
+        {0x800f, 211},
+        {0x8018, 211},
+        {0x801f, 211},
+        {0x8029, 211},
+        {0xc038, 211},
+        {0x8003, 212},
+        {0x8006, 212},
+        {0x800a, 212},
+        {0x800f, 212},
+        {0x8018, 212},
+        {0x801f, 212},
+        {0x8029, 212},
+        {0xc038, 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},
+        {0x8003, 214},
+        {0x8006, 214},
+        {0x800a, 214},
+        {0x800f, 214},
+        {0x8018, 214},
+        {0x801f, 214},
+        {0x8029, 214},
+        {0xc038, 214},
+        {0x8003, 221},
+        {0x8006, 221},
+        {0x800a, 221},
+        {0x800f, 221},
+        {0x8018, 221},
+        {0x801f, 221},
+        {0x8029, 221},
+        {0xc038, 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},
+        {0x8002, 222},
+        {0x8009, 222},
+        {0x8017, 222},
+        {0xc028, 222},
+        {0x8002, 223},
+        {0x8009, 223},
+        {0x8017, 223},
+        {0xc028, 223},
+        {0x8002, 241},
+        {0x8009, 241},
+        {0x8017, 241},
+        {0xc028, 241},
+        {0x8002, 244},
+        {0x8009, 244},
+        {0x8017, 244},
+        {0xc028, 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},
+        {0x8003, 222},
+        {0x8006, 222},
+        {0x800a, 222},
+        {0x800f, 222},
+        {0x8018, 222},
+        {0x801f, 222},
+        {0x8029, 222},
+        {0xc038, 222},
+        {0x8003, 223},
+        {0x8006, 223},
+        {0x800a, 223},
+        {0x800f, 223},
+        {0x8018, 223},
+        {0x801f, 223},
+        {0x8029, 223},
+        {0xc038, 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},
+        {0x8003, 241},
+        {0x8006, 241},
+        {0x800a, 241},
+        {0x800f, 241},
+        {0x8018, 241},
+        {0x801f, 241},
+        {0x8029, 241},
+        {0xc038, 241},
+        {0x8003, 244},
+        {0x8006, 244},
+        {0x800a, 244},
+        {0x800f, 244},
+        {0x8018, 244},
+        {0x801f, 244},
+        {0x8029, 244},
+        {0xc038, 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},
+        {0x8001, 245},
+        {0xc016, 245},
+        {0x8001, 246},
+        {0xc016, 246},
+        {0x8001, 247},
+        {0xc016, 247},
+        {0x8001, 248},
+        {0xc016, 248},
+        {0x8001, 250},
+        {0xc016, 250},
+        {0x8001, 251},
+        {0xc016, 251},
+        {0x8001, 252},
+        {0xc016, 252},
+        {0x8001, 253},
+        {0xc016, 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},
+        {0x8002, 245},
+        {0x8009, 245},
+        {0x8017, 245},
+        {0xc028, 245},
+        {0x8002, 246},
+        {0x8009, 246},
+        {0x8017, 246},
+        {0xc028, 246},
+        {0x8002, 247},
+        {0x8009, 247},
+        {0x8017, 247},
+        {0xc028, 247},
+        {0x8002, 248},
+        {0x8009, 248},
+        {0x8017, 248},
+        {0xc028, 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},
+        {0x8003, 245},
+        {0x8006, 245},
+        {0x800a, 245},
+        {0x800f, 245},
+        {0x8018, 245},
+        {0x801f, 245},
+        {0x8029, 245},
+        {0xc038, 245},
+        {0x8003, 246},
+        {0x8006, 246},
+        {0x800a, 246},
+        {0x800f, 246},
+        {0x8018, 246},
+        {0x801f, 246},
+        {0x8029, 246},
+        {0xc038, 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},
+        {0x8003, 247},
+        {0x8006, 247},
+        {0x800a, 247},
+        {0x800f, 247},
+        {0x8018, 247},
+        {0x801f, 247},
+        {0x8029, 247},
+        {0xc038, 247},
+        {0x8003, 248},
+        {0x8006, 248},
+        {0x800a, 248},
+        {0x800f, 248},
+        {0x8018, 248},
+        {0x801f, 248},
+        {0x8029, 248},
+        {0xc038, 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},
+        {0x8002, 250},
+        {0x8009, 250},
+        {0x8017, 250},
+        {0xc028, 250},
+        {0x8002, 251},
+        {0x8009, 251},
+        {0x8017, 251},
+        {0xc028, 251},
+        {0x8002, 252},
+        {0x8009, 252},
+        {0x8017, 252},
+        {0xc028, 252},
+        {0x8002, 253},
+        {0x8009, 253},
+        {0x8017, 253},
+        {0xc028, 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},
+        {0x8003, 250},
+        {0x8006, 250},
+        {0x800a, 250},
+        {0x800f, 250},
+        {0x8018, 250},
+        {0x801f, 250},
+        {0x8029, 250},
+        {0xc038, 250},
+        {0x8003, 251},
+        {0x8006, 251},
+        {0x800a, 251},
+        {0x800f, 251},
+        {0x8018, 251},
+        {0x801f, 251},
+        {0x8029, 251},
+        {0xc038, 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},
+        {0x8003, 252},
+        {0x8006, 252},
+        {0x800a, 252},
+        {0x800f, 252},
+        {0x8018, 252},
+        {0x801f, 252},
+        {0x8029, 252},
+        {0xc038, 252},
+        {0x8003, 253},
+        {0x8006, 253},
+        {0x800a, 253},
+        {0x800f, 253},
+        {0x8018, 253},
+        {0x801f, 253},
+        {0x8029, 253},
+        {0xc038, 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},
+        {0xc000, 254},
+        {0xe3, 0},
+        {0xe5, 0},
+        {0xe6, 0},
+        {0xe9, 0},
+        {0xea, 0},
+        {0xec, 0},
+        {0xed, 0},
+        {0xf1, 0},
+        {0xf2, 0},
+        {0xf4, 0},
+        {0xf5, 0},
+        {0xf8, 0},
+        {0xf9, 0},
+        {0xfb, 0},
+        {0xfc, 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},
+        {0x8001, 254},
+        {0xc016, 254},
+        {0xc000, 2},
+        {0xc000, 3},
+        {0xc000, 4},
+        {0xc000, 5},
+        {0xc000, 6},
+        {0xc000, 7},
+        {0xc000, 8},
+        {0xc000, 11},
+        {0xc000, 12},
+        {0xc000, 14},
+        {0xc000, 15},
+        {0xc000, 16},
+        {0xc000, 17},
+        {0xc000, 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},
+        {0x8002, 254},
+        {0x8009, 254},
+        {0x8017, 254},
+        {0xc028, 254},
+        {0x8001, 2},
+        {0xc016, 2},
+        {0x8001, 3},
+        {0xc016, 3},
+        {0x8001, 4},
+        {0xc016, 4},
+        {0x8001, 5},
+        {0xc016, 5},
+        {0x8001, 6},
+        {0xc016, 6},
+        {0x8001, 7},
+        {0xc016, 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},
+        {0x8003, 254},
+        {0x8006, 254},
+        {0x800a, 254},
+        {0x800f, 254},
+        {0x8018, 254},
+        {0x801f, 254},
+        {0x8029, 254},
+        {0xc038, 254},
+        {0x8002, 2},
+        {0x8009, 2},
+        {0x8017, 2},
+        {0xc028, 2},
+        {0x8002, 3},
+        {0x8009, 3},
+        {0x8017, 3},
+        {0xc028, 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},
+        {0x8003, 2},
+        {0x8006, 2},
+        {0x800a, 2},
+        {0x800f, 2},
+        {0x8018, 2},
+        {0x801f, 2},
+        {0x8029, 2},
+        {0xc038, 2},
+        {0x8003, 3},
+        {0x8006, 3},
+        {0x800a, 3},
+        {0x800f, 3},
+        {0x8018, 3},
+        {0x801f, 3},
+        {0x8029, 3},
+        {0xc038, 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},
+        {0x8002, 4},
+        {0x8009, 4},
+        {0x8017, 4},
+        {0xc028, 4},
+        {0x8002, 5},
+        {0x8009, 5},
+        {0x8017, 5},
+        {0xc028, 5},
+        {0x8002, 6},
+        {0x8009, 6},
+        {0x8017, 6},
+        {0xc028, 6},
+        {0x8002, 7},
+        {0x8009, 7},
+        {0x8017, 7},
+        {0xc028, 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},
+        {0x8003, 4},
+        {0x8006, 4},
+        {0x800a, 4},
+        {0x800f, 4},
+        {0x8018, 4},
+        {0x801f, 4},
+        {0x8029, 4},
+        {0xc038, 4},
+        {0x8003, 5},
+        {0x8006, 5},
+        {0x800a, 5},
+        {0x800f, 5},
+        {0x8018, 5},
+        {0x801f, 5},
+        {0x8029, 5},
+        {0xc038, 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},
+        {0x8003, 6},
+        {0x8006, 6},
+        {0x800a, 6},
+        {0x800f, 6},
+        {0x8018, 6},
+        {0x801f, 6},
+        {0x8029, 6},
+        {0xc038, 6},
+        {0x8003, 7},
+        {0x8006, 7},
+        {0x800a, 7},
+        {0x800f, 7},
+        {0x8018, 7},
+        {0x801f, 7},
+        {0x8029, 7},
+        {0xc038, 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},
+        {0x8001, 8},
+        {0xc016, 8},
+        {0x8001, 11},
+        {0xc016, 11},
+        {0x8001, 12},
+        {0xc016, 12},
+        {0x8001, 14},
+        {0xc016, 14},
+        {0x8001, 15},
+        {0xc016, 15},
+        {0x8001, 16},
+        {0xc016, 16},
+        {0x8001, 17},
+        {0xc016, 17},
+        {0x8001, 18},
+        {0xc016, 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},
+        {0x8002, 8},
+        {0x8009, 8},
+        {0x8017, 8},
+        {0xc028, 8},
+        {0x8002, 11},
+        {0x8009, 11},
+        {0x8017, 11},
+        {0xc028, 11},
+        {0x8002, 12},
+        {0x8009, 12},
+        {0x8017, 12},
+        {0xc028, 12},
+        {0x8002, 14},
+        {0x8009, 14},
+        {0x8017, 14},
+        {0xc028, 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},
+        {0x8003, 8},
+        {0x8006, 8},
+        {0x800a, 8},
+        {0x800f, 8},
+        {0x8018, 8},
+        {0x801f, 8},
+        {0x8029, 8},
+        {0xc038, 8},
+        {0x8003, 11},
+        {0x8006, 11},
+        {0x800a, 11},
+        {0x800f, 11},
+        {0x8018, 11},
+        {0x801f, 11},
+        {0x8029, 11},
+        {0xc038, 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},
+        {0x8003, 12},
+        {0x8006, 12},
+        {0x800a, 12},
+        {0x800f, 12},
+        {0x8018, 12},
+        {0x801f, 12},
+        {0x8029, 12},
+        {0xc038, 12},
+        {0x8003, 14},
+        {0x8006, 14},
+        {0x800a, 14},
+        {0x800f, 14},
+        {0x8018, 14},
+        {0x801f, 14},
+        {0x8029, 14},
+        {0xc038, 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},
+        {0x8002, 15},
+        {0x8009, 15},
+        {0x8017, 15},
+        {0xc028, 15},
+        {0x8002, 16},
+        {0x8009, 16},
+        {0x8017, 16},
+        {0xc028, 16},
+        {0x8002, 17},
+        {0x8009, 17},
+        {0x8017, 17},
+        {0xc028, 17},
+        {0x8002, 18},
+        {0x8009, 18},
+        {0x8017, 18},
+        {0xc028, 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},
+        {0x8003, 15},
+        {0x8006, 15},
+        {0x800a, 15},
+        {0x800f, 15},
+        {0x8018, 15},
+        {0x801f, 15},
+        {0x8029, 15},
+        {0xc038, 15},
+        {0x8003, 16},
+        {0x8006, 16},
+        {0x800a, 16},
+        {0x800f, 16},
+        {0x8018, 16},
+        {0x801f, 16},
+        {0x8029, 16},
+        {0xc038, 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},
+        {0x8003, 17},
+        {0x8006, 17},
+        {0x800a, 17},
+        {0x800f, 17},
+        {0x8018, 17},
+        {0x801f, 17},
+        {0x8029, 17},
+        {0xc038, 17},
+        {0x8003, 18},
+        {0x8006, 18},
+        {0x800a, 18},
+        {0x800f, 18},
+        {0x8018, 18},
+        {0x801f, 18},
+        {0x8029, 18},
+        {0xc038, 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},
+        {0xc000, 19},
+        {0xc000, 20},
+        {0xc000, 21},
+        {0xc000, 23},
+        {0xc000, 24},
+        {0xc000, 25},
+        {0xc000, 26},
+        {0xc000, 27},
+        {0xc000, 28},
+        {0xc000, 29},
+        {0xc000, 30},
+        {0xc000, 31},
+        {0xc000, 127},
+        {0xc000, 220},
+        {0xc000, 249},
+        {0xfd, 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},
+        {0x8001, 19},
+        {0xc016, 19},
+        {0x8001, 20},
+        {0xc016, 20},
+        {0x8001, 21},
+        {0xc016, 21},
+        {0x8001, 23},
+        {0xc016, 23},
+        {0x8001, 24},
+        {0xc016, 24},
+        {0x8001, 25},
+        {0xc016, 25},
+        {0x8001, 26},
+        {0xc016, 26},
+        {0x8001, 27},
+        {0xc016, 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},
+        {0x8002, 19},
+        {0x8009, 19},
+        {0x8017, 19},
+        {0xc028, 19},
+        {0x8002, 20},
+        {0x8009, 20},
+        {0x8017, 20},
+        {0xc028, 20},
+        {0x8002, 21},
+        {0x8009, 21},
+        {0x8017, 21},
+        {0xc028, 21},
+        {0x8002, 23},
+        {0x8009, 23},
+        {0x8017, 23},
+        {0xc028, 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},
+        {0x8003, 19},
+        {0x8006, 19},
+        {0x800a, 19},
+        {0x800f, 19},
+        {0x8018, 19},
+        {0x801f, 19},
+        {0x8029, 19},
+        {0xc038, 19},
+        {0x8003, 20},
+        {0x8006, 20},
+        {0x800a, 20},
+        {0x800f, 20},
+        {0x8018, 20},
+        {0x801f, 20},
+        {0x8029, 20},
+        {0xc038, 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},
+        {0x8003, 21},
+        {0x8006, 21},
+        {0x800a, 21},
+        {0x800f, 21},
+        {0x8018, 21},
+        {0x801f, 21},
+        {0x8029, 21},
+        {0xc038, 21},
+        {0x8003, 23},
+        {0x8006, 23},
+        {0x800a, 23},
+        {0x800f, 23},
+        {0x8018, 23},
+        {0x801f, 23},
+        {0x8029, 23},
+        {0xc038, 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},
+        {0x8002, 24},
+        {0x8009, 24},
+        {0x8017, 24},
+        {0xc028, 24},
+        {0x8002, 25},
+        {0x8009, 25},
+        {0x8017, 25},
+        {0xc028, 25},
+        {0x8002, 26},
+        {0x8009, 26},
+        {0x8017, 26},
+        {0xc028, 26},
+        {0x8002, 27},
+        {0x8009, 27},
+        {0x8017, 27},
+        {0xc028, 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},
+        {0x8003, 24},
+        {0x8006, 24},
+        {0x800a, 24},
+        {0x800f, 24},
+        {0x8018, 24},
+        {0x801f, 24},
+        {0x8029, 24},
+        {0xc038, 24},
+        {0x8003, 25},
+        {0x8006, 25},
+        {0x800a, 25},
+        {0x800f, 25},
+        {0x8018, 25},
+        {0x801f, 25},
+        {0x8029, 25},
+        {0xc038, 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},
+        {0x8003, 26},
+        {0x8006, 26},
+        {0x800a, 26},
+        {0x800f, 26},
+        {0x8018, 26},
+        {0x801f, 26},
+        {0x8029, 26},
+        {0xc038, 26},
+        {0x8003, 27},
+        {0x8006, 27},
+        {0x800a, 27},
+        {0x800f, 27},
+        {0x8018, 27},
+        {0x801f, 27},
+        {0x8029, 27},
+        {0xc038, 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},
+        {0x8001, 28},
+        {0xc016, 28},
+        {0x8001, 29},
+        {0xc016, 29},
+        {0x8001, 30},
+        {0xc016, 30},
+        {0x8001, 31},
+        {0xc016, 31},
+        {0x8001, 127},
+        {0xc016, 127},
+        {0x8001, 220},
+        {0xc016, 220},
+        {0x8001, 249},
+        {0xc016, 249},
+        {0xfe, 0},
+        {0xff, 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},
+        {0x8002, 28},
+        {0x8009, 28},
+        {0x8017, 28},
+        {0xc028, 28},
+        {0x8002, 29},
+        {0x8009, 29},
+        {0x8017, 29},
+        {0xc028, 29},
+        {0x8002, 30},
+        {0x8009, 30},
+        {0x8017, 30},
+        {0xc028, 30},
+        {0x8002, 31},
+        {0x8009, 31},
+        {0x8017, 31},
+        {0xc028, 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},
+        {0x8003, 28},
+        {0x8006, 28},
+        {0x800a, 28},
+        {0x800f, 28},
+        {0x8018, 28},
+        {0x801f, 28},
+        {0x8029, 28},
+        {0xc038, 28},
+        {0x8003, 29},
+        {0x8006, 29},
+        {0x800a, 29},
+        {0x800f, 29},
+        {0x8018, 29},
+        {0x801f, 29},
+        {0x8029, 29},
+        {0xc038, 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},
+        {0x8003, 30},
+        {0x8006, 30},
+        {0x800a, 30},
+        {0x800f, 30},
+        {0x8018, 30},
+        {0x801f, 30},
+        {0x8029, 30},
+        {0xc038, 30},
+        {0x8003, 31},
+        {0x8006, 31},
+        {0x800a, 31},
+        {0x800f, 31},
+        {0x8018, 31},
+        {0x801f, 31},
+        {0x8029, 31},
+        {0xc038, 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},
+        {0x8002, 127},
+        {0x8009, 127},
+        {0x8017, 127},
+        {0xc028, 127},
+        {0x8002, 220},
+        {0x8009, 220},
+        {0x8017, 220},
+        {0xc028, 220},
+        {0x8002, 249},
+        {0x8009, 249},
+        {0x8017, 249},
+        {0xc028, 249},
+        {0xc000, 10},
+        {0xc000, 13},
+        {0xc000, 22},
+        {0x100, 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},
+        {0x8003, 127},
+        {0x8006, 127},
+        {0x800a, 127},
+        {0x800f, 127},
+        {0x8018, 127},
+        {0x801f, 127},
+        {0x8029, 127},
+        {0xc038, 127},
+        {0x8003, 220},
+        {0x8006, 220},
+        {0x800a, 220},
+        {0x800f, 220},
+        {0x8018, 220},
+        {0x801f, 220},
+        {0x8029, 220},
+        {0xc038, 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},
+        {0x8003, 249},
+        {0x8006, 249},
+        {0x800a, 249},
+        {0x800f, 249},
+        {0x8018, 249},
+        {0x801f, 249},
+        {0x8029, 249},
+        {0xc038, 249},
+        {0x8001, 10},
+        {0xc016, 10},
+        {0x8001, 13},
+        {0xc016, 13},
+        {0x8001, 22},
+        {0xc016, 22},
+        {0x100, 0},
+        {0x100, 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},
+        {0x8002, 10},
+        {0x8009, 10},
+        {0x8017, 10},
+        {0xc028, 10},
+        {0x8002, 13},
+        {0x8009, 13},
+        {0x8017, 13},
+        {0xc028, 13},
+        {0x8002, 22},
+        {0x8009, 22},
+        {0x8017, 22},
+        {0xc028, 22},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 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},
+        {0x8003, 10},
+        {0x8006, 10},
+        {0x800a, 10},
+        {0x800f, 10},
+        {0x8018, 10},
+        {0x801f, 10},
+        {0x8029, 10},
+        {0xc038, 10},
+        {0x8003, 13},
+        {0x8006, 13},
+        {0x800a, 13},
+        {0x800f, 13},
+        {0x8018, 13},
+        {0x801f, 13},
+        {0x8029, 13},
+        {0xc038, 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},
+        {0x8003, 22},
+        {0x8006, 22},
+        {0x800a, 22},
+        {0x800f, 22},
+        {0x8018, 22},
+        {0x801f, 22},
+        {0x8029, 22},
+        {0xc038, 22},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+    },
+    /* 256 */
+    {
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
+        {0x100, 0},
     },
 };
index 81a8a0c..91136a6 100644 (file)
@@ -505,6 +505,84 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) {
   return 1;
 }
 
+/* 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 */
+};
+
+int nghttp2_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;
+}
+
 uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) {
   if (len == 0) {
     return dest;
index 6e8acfd..62f57b6 100644 (file)
@@ -263,11 +263,14 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
       stream->content_length = 0;
       return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
     }
-    if (stream->status_code / 100 == 1 ||
-        (stream->status_code / 100 == 2 &&
-         (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT))) {
+    if (stream->status_code / 100 == 1) {
       return NGHTTP2_ERR_HTTP_HEADER;
     }
+    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
+    if (stream->status_code / 100 == 2 &&
+        (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
+      return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
+    }
     if (stream->content_length != -1) {
       return NGHTTP2_ERR_HTTP_HEADER;
     }
@@ -302,84 +305,6 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
   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) {
@@ -437,7 +362,7 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
 
   if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
       nv->token == NGHTTP2_TOKEN_HOST) {
-    rv = check_authority(nv->value->base, nv->value->len);
+    rv = nghttp2_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 {
index 8946d7d..e53f22d 100644 (file)
@@ -116,3 +116,8 @@ 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;
 }
+
+void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) {
+  option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK;
+  option->max_outbound_ack = val;
+}
index 29e72aa..1f740aa 100644 (file)
@@ -66,6 +66,7 @@ typedef enum {
   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_OPT_MAX_OUTBOUND_ACK = 1 << 11,
 } nghttp2_option_flag;
 
 /**
@@ -81,6 +82,10 @@ struct nghttp2_option {
    */
   size_t max_deflate_dynamic_table_size;
   /**
+   * NGHTTP2_OPT_MAX_OUTBOUND_ACK
+   */
+  size_t max_outbound_ack;
+  /**
    * Bitwise OR of nghttp2_option_flag to determine that which fields
    * are specified.
    */
index ef4932a..9df3d6f 100644 (file)
@@ -457,6 +457,7 @@ static int session_new(nghttp2_session **session_ptr,
   (*session_ptr)->remote_settings.max_concurrent_streams = 100;
 
   (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
+  (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
 
   if (option) {
     if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
@@ -516,6 +517,10 @@ static int session_new(nghttp2_session **session_ptr,
         option->no_closed_streams) {
       (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS;
     }
+
+    if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {
+      (*session_ptr)->max_outbound_ack = option->max_outbound_ack;
+    }
   }
 
   rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
@@ -3619,71 +3624,73 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
 
     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 (subject_stream) {
+        if (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;
+          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;
+            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;
+              }
             }
+          }
 
-            /* header is ignored */
-            DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
+          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);
 
-            rv2 = session_call_error_callback(
+            rv = session_call_error_callback(
                 session, NGHTTP2_ERR_HTTP_HEADER,
-                "Ignoring received invalid HTTP header field: frame type: "
+                "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(rv2)) {
-              return rv2;
+            if (nghttp2_is_fatal(rv)) {
+              return rv;
             }
-          }
-        }
-
-        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;
           }
-
-          rv =
-              session_handle_invalid_stream2(session, subject_stream->stream_id,
-                                             frame, NGHTTP2_ERR_HTTP_HEADER);
-          if (nghttp2_is_fatal(rv)) {
+        }
+        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;
           }
-          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;
         }
       }
     }
@@ -3728,7 +3735,6 @@ static int session_end_stream_headers_received(nghttp2_session *session,
 
 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;
 
@@ -3782,21 +3788,25 @@ static int session_after_header_block_received(nghttp2_session *session) {
         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 (frame->hd.type == NGHTTP2_HEADERS &&
+          (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
+        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
+        /* Don't call nghttp2_session_close_stream_if_shut_rdwr
+           because RST_STREAM has been submitted. */
+      }
+      return 0;
     }
   }
 
-  if (call_cb) {
-    rv = session_call_on_frame_received(session, frame);
-    if (nghttp2_is_fatal(rv)) {
-      return rv;
-    }
+  rv = session_call_on_frame_received(session, frame);
+  if (nghttp2_is_fatal(rv)) {
+    return rv;
   }
 
   if (frame->hd.type != NGHTTP2_HEADERS) {
@@ -4935,7 +4945,6 @@ static int session_process_extension_frame(nghttp2_session *session) {
 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
@@ -4951,20 +4960,22 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
   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;
       }
+
+      nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
+      /* Don't call nghttp2_session_close_stream_if_shut_rdwr because
+         RST_STREAM has been submitted. */
+      return 0;
     }
   }
 
-  if (call_cb) {
-    rv = session_call_on_frame_received(session, frame);
-    if (nghttp2_is_fatal(rv)) {
-      return rv;
-    }
+  rv = session_call_on_frame_received(session, frame);
+  if (nghttp2_is_fatal(rv)) {
+    return rv;
   }
 
   if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
@@ -5402,8 +5413,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
     case NGHTTP2_IB_READ_CLIENT_MAGIC:
       readlen = nghttp2_min(inlen, iframe->payloadleft);
 
-      if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN -
-                     iframe->payloadleft,
+      if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN -
+                                       iframe->payloadleft],
                  in, readlen) != 0) {
         return NGHTTP2_ERR_BAD_CLIENT_MAGIC;
       }
@@ -6855,7 +6866,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
   mem = &session->mem;
 
   if ((flags & NGHTTP2_FLAG_ACK) &&
-      session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
+      session->obq_flood_counter_ >= session->max_outbound_ack) {
     return NGHTTP2_ERR_FLOODED;
   }
 
@@ -7000,7 +7011,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
       return NGHTTP2_ERR_INVALID_ARGUMENT;
     }
 
-    if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
+    if (session->obq_flood_counter_ >= session->max_outbound_ack) {
       return NGHTTP2_ERR_FLOODED;
     }
   }
index 40a8865..90ead9c 100644 (file)
@@ -97,7 +97,7 @@ typedef struct {
    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
+#define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000
 
 /* The default value of maximum number of concurrent streams. */
 #define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu
@@ -209,9 +209,6 @@ struct nghttp2_session {
   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
@@ -261,8 +258,12 @@ struct nghttp2_session {
   size_t num_idle_streams;
   /* The number of bytes allocated for nvbuf */
   size_t nvbuflen;
-  /* Counter for detecting flooding in outbound queue */
+  /* Counter for detecting flooding in outbound queue.  If it exceeds
+     max_outbound_ack, session will be closed. */
   size_t obq_flood_counter_;
+  /* The maximum number of outgoing SETTINGS ACK and PING ACK in
+     outbound queue. */
+  size_t max_outbound_ack;
   /* 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;
index eccd317..dc3a6b1 100644 (file)
@@ -30,6 +30,7 @@
 #include "nghttp2_session.h"
 #include "nghttp2_helper.h"
 #include "nghttp2_debug.h"
+#include "nghttp2_frame.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
@@ -40,7 +41,8 @@
    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)
+#define NGHTTP2_MAX_CYCLE_DISTANCE                                             \
+  ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255)
 
 static int stream_less(const void *lhsx, const void *rhsx) {
   const nghttp2_stream *lhs, *rhs;
@@ -52,11 +54,7 @@ static int stream_less(const void *lhsx, const void *rhsx) {
     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;
+  return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE;
 }
 
 void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
@@ -135,14 +133,14 @@ static int stream_subtree_active(nghttp2_stream *stream) {
 /*
  * Returns next cycle for |stream|.
  */
-static void stream_next_cycle(nghttp2_stream *stream, uint32_t last_cycle) {
-  uint32_t penalty;
+static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
+  uint64_t penalty;
 
-  penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +
+  penalty = (uint64_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;
+  stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight);
 }
 
 static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
@@ -153,7 +151,7 @@ static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
     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,
+    DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id,
            stream->cycle);
 
     DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id,
@@ -239,7 +237,7 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) {
 
     nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
 
-    DEBUGF("stream: stream=%d obq resched cycle=%d\n", stream->stream_id,
+    DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
            stream->cycle);
 
     dep_stream->last_writelen = stream->last_writelen;
@@ -248,9 +246,9 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) {
 
 void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
   nghttp2_stream *dep_stream;
-  uint32_t last_cycle;
+  uint64_t last_cycle;
   int32_t old_weight;
-  uint32_t wlen_penalty;
+  uint64_t wlen_penalty;
 
   if (stream->weight == weight) {
     return;
@@ -273,7 +271,7 @@ void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
 
   nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
 
-  wlen_penalty = (uint32_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT;
+  wlen_penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT;
 
   /* Compute old stream->pending_penalty we used to calculate
      stream->cycle */
@@ -289,9 +287,8 @@ void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
      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) {
+  if (dep_stream->descendant_last_cycle - stream->cycle <=
+      NGHTTP2_MAX_CYCLE_DISTANCE) {
     stream->cycle = dep_stream->descendant_last_cycle;
   }
 
@@ -299,7 +296,7 @@ void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
 
   nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
 
-  DEBUGF("stream: stream=%d obq resched cycle=%d\n", stream->stream_id,
+  DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
          stream->cycle);
 }
 
index fb8dc14..a1b807d 100644 (file)
@@ -148,9 +148,9 @@ struct nghttp2_stream {
   /* Received body so far */
   int64_t recv_content_length;
   /* Base last_cycle for direct descendent streams. */
-  uint32_t descendant_last_cycle;
+  uint64_t descendant_last_cycle;
   /* Next scheduled time to sent item */
-  uint32_t cycle;
+  uint64_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
index d3230f7..d11f1e0 100644 (file)
--- a/ltmain.sh
+++ b/ltmain.sh
@@ -31,7 +31,7 @@
 
 PROGRAM=libtool
 PACKAGE=libtool
-VERSION="2.4.6 Debian-2.4.6-4"
+VERSION="2.4.6 Debian-2.4.6-11"
 package_revision=2.4.6
 
 
@@ -64,7 +64,7 @@ package_revision=2.4.6
 # libraries, which are installed to $pkgauxdir.
 
 # Set a version string for this script.
-scriptversion=2015-10-12.13; # UTC
+scriptversion=2015-01-20.17; # UTC
 
 # General shell script boiler plate, and helper functions.
 # Written by Gary V. Vaughan, 2004
@@ -580,16 +580,16 @@ if test yes = "$_G_HAVE_PLUSEQ_OP"; then
   {
     $debug_cmd
 
-    func_quote_arg pretty "$2"
-    eval "$1+=\\ \$func_quote_arg_result"
+    func_quote_for_eval "$2"
+    eval "$1+=\\ \$func_quote_for_eval_result"
   }'
 else
   func_append_quoted ()
   {
     $debug_cmd
 
-    func_quote_arg pretty "$2"
-    eval "$1=\$$1\\ \$func_quote_arg_result"
+    func_quote_for_eval "$2"
+    eval "$1=\$$1\\ \$func_quote_for_eval_result"
   }
 fi
 
@@ -1091,181 +1091,85 @@ func_relative_path ()
 }
 
 
-# func_quote_portable EVAL ARG
-# ----------------------------
-# Internal function to portably implement func_quote_arg.  Note that we still
-# keep attention to performance here so we as much as possible try to avoid
-# calling sed binary (so far O(N) complexity as long as func_append is O(1)).
-func_quote_portable ()
+# func_quote_for_eval ARG...
+# --------------------------
+# Aesthetically quote ARGs to be evaled later.
+# This function returns two values:
+#   i) func_quote_for_eval_result
+#      double-quoted, suitable for a subsequent eval
+#  ii) func_quote_for_eval_unquoted_result
+#      has all characters that are still active within double
+#      quotes backslashified.
+func_quote_for_eval ()
 {
     $debug_cmd
 
-    func_quote_portable_result=$2
-
-    # one-time-loop (easy break)
-    while true
-    do
-      if $1; then
-        func_quote_portable_result=`$ECHO "$2" | $SED \
-          -e "$sed_double_quote_subst" -e "$sed_double_backslash"`
-        break
-      fi
-
-      # Quote for eval.
-      case $func_quote_portable_result in
+    func_quote_for_eval_unquoted_result=
+    func_quote_for_eval_result=
+    while test 0 -lt $#; do
+      case $1 in
         *[\\\`\"\$]*)
-          case $func_quote_portable_result in
-            *[\[\*\?]*)
-              func_quote_portable_result=`$ECHO "$func_quote_portable_result" | $SED "$sed_quote_subst"`
-              break
-              ;;
-          esac
+         _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
+        *)
+          _G_unquoted_arg=$1 ;;
+      esac
+      if test -n "$func_quote_for_eval_unquoted_result"; then
+       func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
+      else
+        func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
+      fi
 
-          func_quote_portable_old_IFS=$IFS
-          for _G_char in '\' '`' '"' '$'
-          do
-            # STATE($1) PREV($2) SEPARATOR($3)
-            set start "" ""
-            func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy
-            IFS=$_G_char
-            for _G_part in $func_quote_portable_result
-            do
-              case $1 in
-              quote)
-                func_append func_quote_portable_result "$3$2"
-                set quote "$_G_part" "\\$_G_char"
-                ;;
-              start)
-                set first "" ""
-                func_quote_portable_result=
-                ;;
-              first)
-                set quote "$_G_part" ""
-                ;;
-              esac
-            done
-          done
-          IFS=$func_quote_portable_old_IFS
+      case $_G_unquoted_arg in
+        # Double-quote args containing shell metacharacters to delay
+        # word splitting, command substitution and variable expansion
+        # for a subsequent eval.
+        # Many Bourne shells cannot handle close brackets correctly
+        # in scan sets, so we specify it separately.
+        *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \  ]*|*]*|"")
+          _G_quoted_arg=\"$_G_unquoted_arg\"
           ;;
-        *) ;;
+        *)
+          _G_quoted_arg=$_G_unquoted_arg
+         ;;
       esac
-      break
-    done
 
-    func_quote_portable_unquoted_result=$func_quote_portable_result
-    case $func_quote_portable_result in
-      # double-quote args containing shell metacharacters to delay
-      # word splitting, command substitution and variable expansion
-      # for a subsequent eval.
-      # many bourne shells cannot handle close brackets correctly
-      # in scan sets, so we specify it separately.
-      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
-        func_quote_portable_result=\"$func_quote_portable_result\"
-        ;;
-    esac
+      if test -n "$func_quote_for_eval_result"; then
+       func_append func_quote_for_eval_result " $_G_quoted_arg"
+      else
+        func_append func_quote_for_eval_result "$_G_quoted_arg"
+      fi
+      shift
+    done
 }
 
 
-# func_quotefast_eval ARG
-# -----------------------
-# Quote one ARG (internal).  This is equivalent to 'func_quote_arg eval ARG',
-# but optimized for speed.  Result is stored in $func_quotefast_eval.
-if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then
-  func_quotefast_eval ()
-  {
-    printf -v func_quotefast_eval_result %q "$1"
-  }
-else
-  func_quotefast_eval ()
-  {
-    func_quote_portable false "$1"
-    func_quotefast_eval_result=$func_quote_portable_result
-  }
-fi
-
+# func_quote_for_expand ARG
+# -------------------------
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+    $debug_cmd
 
-# func_quote_arg MODEs ARG
-# ------------------------
-# Quote one ARG to be evaled later.  MODEs argument may contain zero ore more
-# specifiers listed below separated by ',' character.  This function returns two
-# values:
-#   i) func_quote_arg_result
-#      double-quoted (when needed), suitable for a subsequent eval
-#  ii) func_quote_arg_unquoted_result
-#      has all characters that are still active within double
-#      quotes backslashified.  Available only if 'unquoted' is specified.
-#
-# Available modes:
-# ----------------
-# 'eval' (default)
-#       - escape shell special characters
-# 'expand'
-#       - the same as 'eval';  but do not quote variable references
-# 'pretty'
-#       - request aesthetic output, i.e. '"a b"' instead of 'a\ b'.  This might
-#         later used in func_quote to get output like: 'echo "a b"' instead of
-#         'echo a\ b'.  This is slower than default on some shells.
-# 'unquoted'
-#       - produce also $func_quote_arg_unquoted_result which does not contain
-#         wrapping double-quotes.
-#
-# Examples for 'func_quote_arg pretty,unquoted string':
-#
-#   string      | *_result              | *_unquoted_result
-#   ------------+-----------------------+-------------------
-#   "           | \"                    | \"
-#   a b         | "a b"                 | a b
-#   "a b"       | "\"a b\""             | \"a b\"
-#   *           | "*"                   | *
-#   z="${x-$y}" | "z=\"\${x-\$y}\""     | z=\"\${x-\$y}\"
-#
-# Examples for 'func_quote_arg pretty,unquoted,expand string':
-#
-#   string        |   *_result          |  *_unquoted_result
-#   --------------+---------------------+--------------------
-#   z="${x-$y}"   | "z=\"${x-$y}\""     | z=\"${x-$y}\"
-func_quote_arg ()
-{
-    _G_quote_expand=false
-    case ,$1, in
-      *,expand,*)
-        _G_quote_expand=:
-        ;;
+    case $1 in
+      *[\\\`\"]*)
+       _G_arg=`$ECHO "$1" | $SED \
+           -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
+      *)
+        _G_arg=$1 ;;
     esac
 
-    case ,$1, in
-      *,pretty,*|*,expand,*|*,unquoted,*)
-        func_quote_portable $_G_quote_expand "$2"
-        func_quote_arg_result=$func_quote_portable_result
-        func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result
-        ;;
-      *)
-        # Faster quote-for-eval for some shells.
-        func_quotefast_eval "$2"
-        func_quote_arg_result=$func_quotefast_eval_result
+    case $_G_arg in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting and command substitution for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+        _G_arg=\"$_G_arg\"
         ;;
     esac
-}
 
-
-# func_quote MODEs ARGs...
-# ------------------------
-# Quote all ARGs to be evaled later and join them into single command.  See
-# func_quote_arg's description for more info.
-func_quote ()
-{
-    $debug_cmd
-    _G_func_quote_mode=$1 ; shift
-    func_quote_result=
-    while test 0 -lt $#; do
-      func_quote_arg "$_G_func_quote_mode" "$1"
-      if test -n "$func_quote_result"; then
-        func_append func_quote_result " $func_quote_arg_result"
-      else
-        func_append func_quote_result "$func_quote_arg_result"
-      fi
-      shift
-    done
+    func_quote_for_expand_result=$_G_arg
 }
 
 
@@ -1311,8 +1215,8 @@ func_show_eval ()
     _G_cmd=$1
     _G_fail_exp=${2-':'}
 
-    func_quote_arg pretty,expand "$_G_cmd"
-    eval "func_notquiet $func_quote_arg_result"
+    func_quote_for_expand "$_G_cmd"
+    eval "func_notquiet $func_quote_for_expand_result"
 
     $opt_dry_run || {
       eval "$_G_cmd"
@@ -1337,8 +1241,8 @@ func_show_eval_locale ()
     _G_fail_exp=${2-':'}
 
     $opt_quiet || {
-      func_quote_arg expand,pretty "$_G_cmd"
-      eval "func_echo $func_quote_arg_result"
+      func_quote_for_expand "$_G_cmd"
+      eval "func_echo $func_quote_for_expand_result"
     }
 
     $opt_dry_run || {
@@ -1466,7 +1370,7 @@ func_lt_ver ()
 #! /bin/sh
 
 # Set a version string for this script.
-scriptversion=2015-10-12.13; # UTC
+scriptversion=2015-10-07.11; # UTC
 
 # A portable, pluggable option parser for Bourne shell.
 # Written by Gary V. Vaughan, 2010
@@ -1676,8 +1580,8 @@ func_run_hooks ()
 #    '
 #        # No change in '$@' (ignored completely by this hook).  There is
 #        # no need to do the equivalent (but slower) action:
-#        # func_quote eval ${1+"$@"}
-#        # my_options_prep_result=$func_quote_result
+#        # func_quote_for_eval ${1+"$@"}
+#        # my_options_prep_result=$func_quote_for_eval_result
 #        false
 #    }
 #    func_add_hook func_options_prep my_options_prep
@@ -1713,8 +1617,8 @@ func_run_hooks ()
 #        done
 #
 #        if $args_changed; then
-#          func_quote eval ${1+"$@"}
-#          my_silent_option_result=$func_quote_result
+#          func_quote_for_eval ${1+"$@"}
+#          my_silent_option_result=$func_quote_for_eval_result
 #        fi
 #
 #        $args_changed
@@ -1781,8 +1685,8 @@ func_options ()
     if $_G_rc_options; then
       func_options_result=$_G_res_var
     else
-      func_quote eval ${1+"$@"}
-      func_options_result=$func_quote_result
+      func_quote_for_eval ${1+"$@"}
+      func_options_result=$func_quote_for_eval_result
     fi
 
     $_G_rc_options
@@ -1925,8 +1829,8 @@ func_parse_options ()
 
     if $_G_rc_parse_options; then
       # save modified positional parameters for caller
-      func_quote eval ${1+"$@"}
-      func_parse_options_result=$func_quote_result
+      func_quote_for_eval ${1+"$@"}
+      func_parse_options_result=$func_quote_for_eval_result
     fi
 
     $_G_rc_parse_options
@@ -2237,7 +2141,7 @@ include the following information:
        compiler:       $LTCC
        compiler flags: $LTCFLAGS
        linker:         $LD (gnu? $with_gnu_ld)
-       version:        $progname $scriptversion Debian-2.4.6-4
+       version:        $progname $scriptversion Debian-2.4.6-11
        automake:       `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
        autoconf:       `($AUTOCONF --version) 2>/dev/null |$SED 1q`
 
@@ -2471,8 +2375,8 @@ libtool_options_prep ()
 
     if $_G_rc_lt_options_prep; then
       # Pass back the list of options.
-      func_quote eval ${1+"$@"}
-      libtool_options_prep_result=$func_quote_result
+      func_quote_for_eval ${1+"$@"}
+      libtool_options_prep_result=$func_quote_for_eval_result
     fi
 
     $_G_rc_lt_options_prep
@@ -2578,8 +2482,8 @@ libtool_parse_options ()
 
     if $_G_rc_lt_parse_options; then
       # save modified positional parameters for caller
-      func_quote eval ${1+"$@"}
-      libtool_parse_options_result=$func_quote_result
+      func_quote_for_eval ${1+"$@"}
+      libtool_parse_options_result=$func_quote_for_eval_result
     fi
 
     $_G_rc_lt_parse_options
@@ -2639,8 +2543,8 @@ libtool_validate_options ()
     }
 
     # Pass back the unparsed argument list
-    func_quote eval ${1+"$@"}
-    libtool_validate_options_result=$func_quote_result
+    func_quote_for_eval ${1+"$@"}
+    libtool_validate_options_result=$func_quote_for_eval_result
 }
 func_add_hook func_validate_options libtool_validate_options
 
@@ -3606,8 +3510,8 @@ func_mode_compile ()
       esac
     done
 
-    func_quote_arg pretty "$libobj"
-    test "X$libobj" != "X$func_quote_arg_result" \
+    func_quote_for_eval "$libobj"
+    test "X$libobj" != "X$func_quote_for_eval_result" \
       && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'   &()|`$[]' \
       && func_warning "libobj name '$libobj' may not contain shell special characters."
     func_dirname_and_basename "$obj" "/" ""
@@ -3680,8 +3584,8 @@ compiler."
 
     func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
     srcfile=$func_to_tool_file_result
-    func_quote_arg pretty "$srcfile"
-    qsrcfile=$func_quote_arg_result
+    func_quote_for_eval "$srcfile"
+    qsrcfile=$func_quote_for_eval_result
 
     # Only build a PIC object if we are building libtool libraries.
     if test yes = "$build_libtool_libs"; then
@@ -4284,8 +4188,8 @@ func_mode_install ()
        case $nonopt in *shtool*) :;; *) false;; esac
     then
       # Aesthetically quote it.
-      func_quote_arg pretty "$nonopt"
-      install_prog="$func_quote_arg_result "
+      func_quote_for_eval "$nonopt"
+      install_prog="$func_quote_for_eval_result "
       arg=$1
       shift
     else
@@ -4295,8 +4199,8 @@ func_mode_install ()
 
     # The real first argument should be the name of the installation program.
     # Aesthetically quote it.
-    func_quote_arg pretty "$arg"
-    func_append install_prog "$func_quote_arg_result"
+    func_quote_for_eval "$arg"
+    func_append install_prog "$func_quote_for_eval_result"
     install_shared_prog=$install_prog
     case " $install_prog " in
       *[\\\ /]cp\ *) install_cp=: ;;
@@ -4353,12 +4257,12 @@ func_mode_install ()
       esac
 
       # Aesthetically quote the argument.
-      func_quote_arg pretty "$arg"
-      func_append install_prog " $func_quote_arg_result"
+      func_quote_for_eval "$arg"
+      func_append install_prog " $func_quote_for_eval_result"
       if test -n "$arg2"; then
-       func_quote_arg pretty "$arg2"
+       func_quote_for_eval "$arg2"
       fi
-      func_append install_shared_prog " $func_quote_arg_result"
+      func_append install_shared_prog " $func_quote_for_eval_result"
     done
 
     test -z "$install_prog" && \
@@ -4369,8 +4273,8 @@ func_mode_install ()
 
     if test -n "$install_override_mode" && $no_mode; then
       if $install_cp; then :; else
-       func_quote_arg pretty "$install_override_mode"
-       func_append install_shared_prog " -m $func_quote_arg_result"
+       func_quote_for_eval "$install_override_mode"
+       func_append install_shared_prog " -m $func_quote_for_eval_result"
       fi
     fi
 
@@ -4666,8 +4570,8 @@ func_mode_install ()
                relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
 
                $opt_quiet || {
-                 func_quote_arg expand,pretty "$relink_command"
-                 eval "func_echo $func_quote_arg_result"
+                 func_quote_for_expand "$relink_command"
+                 eval "func_echo $func_quote_for_expand_result"
                }
                if eval "$relink_command"; then :
                  else
@@ -5446,8 +5350,7 @@ else
   if test \"\$libtool_execute_magic\" != \"$magic\"; then
     file=\"\$0\""
 
-    func_quote_arg pretty "$ECHO"
-    qECHO=$func_quote_arg_result
+    qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
     $ECHO "\
 
 # A function that is used when there is no print builtin or printf.
@@ -5457,7 +5360,7 @@ func_fallback_echo ()
 \$1
 _LTECHO_EOF'
 }
-    ECHO=$qECHO
+    ECHO=\"$qECHO\"
   fi
 
 # Very basic option parsing. These options are (a) specific to
@@ -6800,9 +6703,9 @@ func_mode_link ()
     while test "$#" -gt 0; do
       arg=$1
       shift
-      func_quote_arg pretty,unquoted "$arg"
-      qarg=$func_quote_arg_unquoted_result
-      func_append libtool_args " $func_quote_arg_result"
+      func_quote_for_eval "$arg"
+      qarg=$func_quote_for_eval_unquoted_result
+      func_append libtool_args " $func_quote_for_eval_result"
 
       # If the previous option needs an argument, assign it.
       if test -n "$prev"; then
@@ -7400,9 +7303,9 @@ func_mode_link ()
        save_ifs=$IFS; IFS=,
        for flag in $args; do
          IFS=$save_ifs
-          func_quote_arg pretty "$flag"
-         func_append arg " $func_quote_arg_result"
-         func_append compiler_flags " $func_quote_arg_result"
+          func_quote_for_eval "$flag"
+         func_append arg " $func_quote_for_eval_result"
+         func_append compiler_flags " $func_quote_for_eval_result"
        done
        IFS=$save_ifs
        func_stripname ' ' '' "$arg"
@@ -7416,10 +7319,10 @@ func_mode_link ()
        save_ifs=$IFS; IFS=,
        for flag in $args; do
          IFS=$save_ifs
-          func_quote_arg pretty "$flag"
-         func_append arg " $wl$func_quote_arg_result"
-         func_append compiler_flags " $wl$func_quote_arg_result"
-         func_append linker_flags " $func_quote_arg_result"
+          func_quote_for_eval "$flag"
+         func_append arg " $wl$func_quote_for_eval_result"
+         func_append compiler_flags " $wl$func_quote_for_eval_result"
+         func_append linker_flags " $func_quote_for_eval_result"
        done
        IFS=$save_ifs
        func_stripname ' ' '' "$arg"
@@ -7443,8 +7346,8 @@ func_mode_link ()
 
       # -msg_* for osf cc
       -msg_*)
-       func_quote_arg pretty "$arg"
-       arg=$func_quote_arg_result
+       func_quote_for_eval "$arg"
+       arg=$func_quote_for_eval_result
        ;;
 
       # Flags to be passed through unchanged, with rationale:
@@ -7464,12 +7367,13 @@ func_mode_link ()
       # -specs=*             GCC specs files
       # -stdlib=*            select c++ std lib with clang
       # -fsanitize=*         Clang/GCC memory and address sanitizer
+      # -fuse-ld=*           Linker select flags for GCC
       -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
       -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
       -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
-      -specs=*|-fsanitize=*)
-        func_quote_arg pretty "$arg"
-       arg=$func_quote_arg_result
+      -specs=*|-fsanitize=*|-fuse-ld=*)
+        func_quote_for_eval "$arg"
+       arg=$func_quote_for_eval_result
         func_append compile_command " $arg"
         func_append finalize_command " $arg"
         func_append compiler_flags " $arg"
@@ -7490,15 +7394,15 @@ func_mode_link ()
          continue
         else
          # Otherwise treat like 'Some other compiler flag' below
-         func_quote_arg pretty "$arg"
-         arg=$func_quote_arg_result
+         func_quote_for_eval "$arg"
+         arg=$func_quote_for_eval_result
         fi
        ;;
 
       # Some other compiler flag.
       -* | +*)
-        func_quote_arg pretty "$arg"
-       arg=$func_quote_arg_result
+        func_quote_for_eval "$arg"
+       arg=$func_quote_for_eval_result
        ;;
 
       *.$objext)
@@ -7618,8 +7522,8 @@ func_mode_link ()
       *)
        # Unknown arguments in both finalize_command and compile_command need
        # to be aesthetically quoted because they are evaled later.
-       func_quote_arg pretty "$arg"
-       arg=$func_quote_arg_result
+       func_quote_for_eval "$arg"
+       arg=$func_quote_for_eval_result
        ;;
       esac # arg
 
@@ -10131,8 +10035,8 @@ EOF
            for cmd in $concat_cmds; do
              IFS=$save_ifs
              $opt_quiet || {
-                 func_quote_arg expand,pretty "$cmd"
-                 eval "func_echo $func_quote_arg_result"
+                 func_quote_for_expand "$cmd"
+                 eval "func_echo $func_quote_for_expand_result"
              }
              $opt_dry_run || eval "$cmd" || {
                lt_exit=$?
@@ -10225,8 +10129,8 @@ EOF
          eval cmd=\"$cmd\"
          IFS=$save_ifs
          $opt_quiet || {
-           func_quote_arg expand,pretty "$cmd"
-           eval "func_echo $func_quote_arg_result"
+           func_quote_for_expand "$cmd"
+           eval "func_echo $func_quote_for_expand_result"
          }
          $opt_dry_run || eval "$cmd" || {
            lt_exit=$?
@@ -10700,12 +10604,12 @@ EOF
          elif eval var_value=\$$var; test -z "$var_value"; then
            relink_command="$var=; export $var; $relink_command"
          else
-           func_quote_arg pretty "$var_value"
-           relink_command="$var=$func_quote_arg_result; export $var; $relink_command"
+           func_quote_for_eval "$var_value"
+           relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
          fi
        done
-       func_quote_arg pretty,unquoted "(cd `pwd`; $relink_command)"
-       relink_command=$func_quote_arg_unquoted_result
+       relink_command="(cd `pwd`; $relink_command)"
+       relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
       fi
 
       # Only actually do things if not in dry run mode.
@@ -10945,14 +10849,13 @@ EOF
        elif eval var_value=\$$var; test -z "$var_value"; then
          relink_command="$var=; export $var; $relink_command"
        else
-         func_quote_arg pretty,unquoted "$var_value"
-         relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command"
+         func_quote_for_eval "$var_value"
+         relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
        fi
       done
       # Quote the link command for shipping.
       relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
-      func_quote_arg pretty,unquoted "$relink_command"
-      relink_command=$func_quote_arg_unquoted_result
+      relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
       if test yes = "$hardcode_automatic"; then
        relink_command=
       fi
diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644 (file)
index 0000000..9e9eaed
--- /dev/null
@@ -0,0 +1,948 @@
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the specified
+#   version of the C++ standard.  If necessary, add switches to CXX and
+#   CXXCPP to enable support.  VERSION may be '11' (for the C++11 standard)
+#   or '14' (for the C++14 standard).
+#
+#   The second 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 third argument, if specified 'mandatory' or if left unspecified,
+#   indicates that baseline support for the specified C++ standard 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_CXX${VERSION} 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>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#   Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@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 10
+
+dnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl  (serial version number 13).
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+  m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+        [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+        [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+        [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$2], [], [],
+        [$2], [ext], [],
+        [$2], [noext], [],
+        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+        [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+  AC_LANG_PUSH([C++])dnl
+  ac_success=no
+
+  m4_if([$2], [noext], [], [dnl
+  if test x$ac_success = xno; then
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      switch="-std=gnu++${alternative}"
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                     $cachevar,
+        [ac_save_CXX="$CXX"
+         CXX="$CXX $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXX="$ac_save_CXX"])
+      if eval test x\$$cachevar = xyes; then
+        CXX="$CXX $switch"
+        if test -n "$CXXCPP" ; then
+          CXXCPP="$CXXCPP $switch"
+        fi
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+
+  m4_if([$2], [ext], [], [dnl
+  if test x$ac_success = xno; then
+    dnl HP's aCC needs +std=c++11 according to:
+    dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+    dnl Cray's crayCC needs "-h std=c++11"
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+        cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+        AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                       $cachevar,
+          [ac_save_CXX="$CXX"
+           CXX="$CXX $switch"
+           AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+            [eval $cachevar=yes],
+            [eval $cachevar=no])
+           CXX="$ac_save_CXX"])
+        if eval test x\$$cachevar = xyes; then
+          CXX="$CXX $switch"
+          if test -n "$CXXCPP" ; then
+            CXXCPP="$CXXCPP $switch"
+          fi
+          ac_success=yes
+          break
+        fi
+      done
+      if test x$ac_success = xyes; then
+        break
+      fi
+    done
+  fi])
+  AC_LANG_POP([C++])
+  if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+    if test x$ac_success = xno; then
+      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+    fi
+  fi
+  if test x$ac_success = xno; then
+    HAVE_CXX$1=0
+    AC_MSG_NOTICE([No compiler with C++$1 support was found])
+  else
+    HAVE_CXX$1=1
+    AC_DEFINE(HAVE_CXX$1,1,
+              [define if the compiler supports basic C++$1 syntax])
+  fi
+  AC_SUBST(HAVE_CXX$1)
+])
+
+
+dnl  Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl  Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl  Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+  namespace test_static_assert
+  {
+
+    template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+  }
+
+  namespace test_final_override
+  {
+
+    struct Base
+    {
+      virtual void f() {}
+    };
+
+    struct Derived : public Base
+    {
+      virtual void f() override {}
+    };
+
+  }
+
+  namespace test_double_right_angle_brackets
+  {
+
+    template < typename T >
+    struct check {};
+
+    typedef check<void> single_type;
+    typedef check<check<void>> double_type;
+    typedef check<check<check<void>>> triple_type;
+    typedef check<check<check<check<void>>>> quadruple_type;
+
+  }
+
+  namespace test_decltype
+  {
+
+    int
+    f()
+    {
+      int a = 1;
+      decltype(a) b = 2;
+      return a + b;
+    }
+
+  }
+
+  namespace test_type_deduction
+  {
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static const bool value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static const bool value = true;
+    };
+
+    template < typename T1, typename T2 >
+    auto
+    add(T1 a1, T2 a2) -> decltype(a1 + a2)
+    {
+      return a1 + a2;
+    }
+
+    int
+    test(const int c, volatile int v)
+    {
+      static_assert(is_same<int, decltype(0)>::value == true, "");
+      static_assert(is_same<int, decltype(c)>::value == false, "");
+      static_assert(is_same<int, decltype(v)>::value == false, "");
+      auto ac = c;
+      auto av = v;
+      auto sumi = ac + av + 'x';
+      auto sumf = ac + av + 1.0;
+      static_assert(is_same<int, decltype(ac)>::value == true, "");
+      static_assert(is_same<int, decltype(av)>::value == true, "");
+      static_assert(is_same<int, decltype(sumi)>::value == true, "");
+      static_assert(is_same<int, decltype(sumf)>::value == false, "");
+      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+      return (sumf > 0.0) ? sumi : add(c, v);
+    }
+
+  }
+
+  namespace test_noexcept
+  {
+
+    int f() { return 0; }
+    int g() noexcept { return 0; }
+
+    static_assert(noexcept(f()) == false, "");
+    static_assert(noexcept(g()) == true, "");
+
+  }
+
+  namespace test_constexpr
+  {
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+    {
+      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+    }
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c(const CharT *const s) noexcept
+    {
+      return strlen_c_r(s, 0UL);
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("1") == 1UL, "");
+    static_assert(strlen_c("example") == 7UL, "");
+    static_assert(strlen_c("another\0example") == 7UL, "");
+
+  }
+
+  namespace test_rvalue_references
+  {
+
+    template < int N >
+    struct answer
+    {
+      static constexpr int value = N;
+    };
+
+    answer<1> f(int&)       { return answer<1>(); }
+    answer<2> f(const int&) { return answer<2>(); }
+    answer<3> f(int&&)      { return answer<3>(); }
+
+    void
+    test()
+    {
+      int i = 0;
+      const int c = 0;
+      static_assert(decltype(f(i))::value == 1, "");
+      static_assert(decltype(f(c))::value == 2, "");
+      static_assert(decltype(f(0))::value == 3, "");
+    }
+
+  }
+
+  namespace test_uniform_initialization
+  {
+
+    struct test
+    {
+      static const int zero {};
+      static const int one {1};
+    };
+
+    static_assert(test::zero == 0, "");
+    static_assert(test::one == 1, "");
+
+  }
+
+  namespace test_lambdas
+  {
+
+    void
+    test1()
+    {
+      auto lambda1 = [](){};
+      auto lambda2 = lambda1;
+      lambda1();
+      lambda2();
+    }
+
+    int
+    test2()
+    {
+      auto a = [](int i, int j){ return i + j; }(1, 2);
+      auto b = []() -> int { return '0'; }();
+      auto c = [=](){ return a + b; }();
+      auto d = [&](){ return c; }();
+      auto e = [a, &b](int x) mutable {
+        const auto identity = [](int y){ return y; };
+        for (auto i = 0; i < a; ++i)
+          a += b--;
+        return x + identity(a + b);
+      }(0);
+      return a + b + c + d + e;
+    }
+
+    int
+    test3()
+    {
+      const auto nullary = [](){ return 0; };
+      const auto unary = [](int x){ return x; };
+      using nullary_t = decltype(nullary);
+      using unary_t = decltype(unary);
+      const auto higher1st = [](nullary_t f){ return f(); };
+      const auto higher2nd = [unary](nullary_t f1){
+        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+      };
+      return higher1st(nullary) + higher2nd(nullary)(unary);
+    }
+
+  }
+
+  namespace test_variadic_templates
+  {
+
+    template <int...>
+    struct sum;
+
+    template <int N0, int... N1toN>
+    struct sum<N0, N1toN...>
+    {
+      static constexpr auto value = N0 + sum<N1toN...>::value;
+    };
+
+    template <>
+    struct sum<>
+    {
+      static constexpr auto value = 0;
+    };
+
+    static_assert(sum<>::value == 0, "");
+    static_assert(sum<1>::value == 1, "");
+    static_assert(sum<23>::value == 23, "");
+    static_assert(sum<1, 2>::value == 3, "");
+    static_assert(sum<5, 5, 11>::value == 21, "");
+    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+  }
+
+  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+  // because of this.
+  namespace test_template_alias_sfinae
+  {
+
+    struct foo {};
+
+    template<typename T>
+    using member = typename T::member_type;
+
+    template<typename T>
+    void func(...) {}
+
+    template<typename T>
+    void func(member<T>*) {}
+
+    void test();
+
+    void test() { func<foo>(0); }
+
+  }
+
+}  // namespace cxx11
+
+#endif  // __cplusplus >= 201103L
+
+]])
+
+
+dnl  Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+  namespace test_polymorphic_lambdas
+  {
+
+    int
+    test()
+    {
+      const auto lambda = [](auto&&... args){
+        const auto istiny = [](auto x){
+          return (sizeof(x) == 1UL) ? 1 : 0;
+        };
+        const int aretiny[] = { istiny(args)... };
+        return aretiny[0];
+      };
+      return lambda(1, 1L, 1.0f, '1');
+    }
+
+  }
+
+  namespace test_binary_literals
+  {
+
+    constexpr auto ivii = 0b0000000000101010;
+    static_assert(ivii == 42, "wrong value");
+
+  }
+
+  namespace test_generalized_constexpr
+  {
+
+    template < typename CharT >
+    constexpr unsigned long
+    strlen_c(const CharT *const s) noexcept
+    {
+      auto length = 0UL;
+      for (auto p = s; *p; ++p)
+        ++length;
+      return length;
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("x") == 1UL, "");
+    static_assert(strlen_c("test") == 4UL, "");
+    static_assert(strlen_c("another\0test") == 7UL, "");
+
+  }
+
+  namespace test_lambda_init_capture
+  {
+
+    int
+    test()
+    {
+      auto x = 0;
+      const auto lambda1 = [a = x](int b){ return a + b; };
+      const auto lambda2 = [a = lambda1(x)](){ return a; };
+      return lambda2();
+    }
+
+  }
+
+  namespace test_digit_separators
+  {
+
+    constexpr auto ten_million = 100'000'000;
+    static_assert(ten_million == 100000000, "");
+
+  }
+
+  namespace test_return_type_deduction
+  {
+
+    auto f(int& x) { return x; }
+    decltype(auto) g(int& x) { return x; }
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static constexpr auto value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static constexpr auto value = true;
+    };
+
+    int
+    test()
+    {
+      auto x = 0;
+      static_assert(is_same<int, decltype(f(x))>::value, "");
+      static_assert(is_same<int&, decltype(g(x))>::value, "");
+      return x;
+    }
+
+  }
+
+}  // namespace cxx14
+
+#endif  // __cplusplus >= 201402L
+
+]])
+
+
+dnl  Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201703L
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+  namespace test_constexpr_lambdas
+  {
+
+    constexpr int foo = [](){return 42;}();
+
+  }
+
+  namespace test::nested_namespace::definitions
+  {
+
+  }
+
+  namespace test_fold_expression
+  {
+
+    template<typename... Args>
+    int multiply(Args... args)
+    {
+      return (args * ... * 1);
+    }
+
+    template<typename... Args>
+    bool all(Args... args)
+    {
+      return (args && ...);
+    }
+
+  }
+
+  namespace test_extended_static_assert
+  {
+
+    static_assert (true);
+
+  }
+
+  namespace test_auto_brace_init_list
+  {
+
+    auto foo = {5};
+    auto bar {5};
+
+    static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+    static_assert(std::is_same<int, decltype(bar)>::value);
+  }
+
+  namespace test_typename_in_template_template_parameter
+  {
+
+    template<template<typename> typename X> struct D;
+
+  }
+
+  namespace test_fallthrough_nodiscard_maybe_unused_attributes
+  {
+
+    int f1()
+    {
+      return 42;
+    }
+
+    [[nodiscard]] int f2()
+    {
+      [[maybe_unused]] auto unused = f1();
+
+      switch (f1())
+      {
+      case 17:
+        f1();
+        [[fallthrough]];
+      case 42:
+        f1();
+      }
+      return f1();
+    }
+
+  }
+
+  namespace test_extended_aggregate_initialization
+  {
+
+    struct base1
+    {
+      int b1, b2 = 42;
+    };
+
+    struct base2
+    {
+      base2() {
+        b3 = 42;
+      }
+      int b3;
+    };
+
+    struct derived : base1, base2
+    {
+        int d;
+    };
+
+    derived d1 {{1, 2}, {}, 4};  // full initialization
+    derived d2 {{}, {}, 4};      // value-initialized bases
+
+  }
+
+  namespace test_general_range_based_for_loop
+  {
+
+    struct iter
+    {
+      int i;
+
+      int& operator* ()
+      {
+        return i;
+      }
+
+      const int& operator* () const
+      {
+        return i;
+      }
+
+      iter& operator++()
+      {
+        ++i;
+        return *this;
+      }
+    };
+
+    struct sentinel
+    {
+      int i;
+    };
+
+    bool operator== (const iter& i, const sentinel& s)
+    {
+      return i.i == s.i;
+    }
+
+    bool operator!= (const iter& i, const sentinel& s)
+    {
+      return !(i == s);
+    }
+
+    struct range
+    {
+      iter begin() const
+      {
+        return {0};
+      }
+
+      sentinel end() const
+      {
+        return {5};
+      }
+    };
+
+    void f()
+    {
+      range r {};
+
+      for (auto i : r)
+      {
+        [[maybe_unused]] auto v = i;
+      }
+    }
+
+  }
+
+  namespace test_lambda_capture_asterisk_this_by_value
+  {
+
+    struct t
+    {
+      int i;
+      int foo()
+      {
+        return [*this]()
+        {
+          return i;
+        }();
+      }
+    };
+
+  }
+
+  namespace test_enum_class_construction
+  {
+
+    enum class byte : unsigned char
+    {};
+
+    byte foo {42};
+
+  }
+
+  namespace test_constexpr_if
+  {
+
+    template <bool cond>
+    int f ()
+    {
+      if constexpr(cond)
+      {
+        return 13;
+      }
+      else
+      {
+        return 42;
+      }
+    }
+
+  }
+
+  namespace test_selection_statement_with_initializer
+  {
+
+    int f()
+    {
+      return 13;
+    }
+
+    int f2()
+    {
+      if (auto i = f(); i > 0)
+      {
+        return 3;
+      }
+
+      switch (auto i = f(); i + 4)
+      {
+      case 17:
+        return 2;
+
+      default:
+        return 1;
+      }
+    }
+
+  }
+
+  namespace test_template_argument_deduction_for_class_templates
+  {
+
+    template <typename T1, typename T2>
+    struct pair
+    {
+      pair (T1 p1, T2 p2)
+        : m1 {p1},
+          m2 {p2}
+      {}
+
+      T1 m1;
+      T2 m2;
+    };
+
+    void f()
+    {
+      [[maybe_unused]] auto p = pair{13, 42u};
+    }
+
+  }
+
+  namespace test_non_type_auto_template_parameters
+  {
+
+    template <auto n>
+    struct B
+    {};
+
+    B<5> b1;
+    B<'a'> b2;
+
+  }
+
+  namespace test_structured_bindings
+  {
+
+    int arr[2] = { 1, 2 };
+    std::pair<int, int> pr = { 1, 2 };
+
+    auto f1() -> int(&)[2]
+    {
+      return arr;
+    }
+
+    auto f2() -> std::pair<int, int>&
+    {
+      return pr;
+    }
+
+    struct S
+    {
+      int x1 : 2;
+      volatile double y1;
+    };
+
+    S f3()
+    {
+      return {};
+    }
+
+    auto [ x1, y1 ] = f1();
+    auto& [ xr1, yr1 ] = f1();
+    auto [ x2, y2 ] = f2();
+    auto& [ xr2, yr2 ] = f2();
+    const auto [ x3, y3 ] = f3();
+
+  }
+
+  namespace test_exception_spec_type_system
+  {
+
+    struct Good {};
+    struct Bad {};
+
+    void g1() noexcept;
+    void g2();
+
+    template<typename T>
+    Bad
+    f(T*, T*);
+
+    template<typename T1, typename T2>
+    Good
+    f(T1*, T2*);
+
+    static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+  }
+
+  namespace test_inline_variables
+  {
+
+    template<class T> void f(T)
+    {}
+
+    template<class T> inline T g(T)
+    {
+      return T{};
+    }
+
+    template<> inline void f<>(int)
+    {}
+
+    template<> int g<>(int)
+    {
+      return 5;
+    }
+
+  }
+
+}  // namespace cxx17
+
+#endif  // __cplusplus < 201703L
+
+]])
diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4
deleted file mode 100644 (file)
index af37acd..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-# ============================================================================
-#  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 e67ed69..9d6dd9f 100644 (file)
@@ -4063,7 +4063,8 @@ _LT_EOF
   if AC_TRY_EVAL(ac_compile); then
     # Now try to grab the symbols.
     nlist=conftest.nm
-    if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
+    $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD
+    if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then
       # Try sorting and uniquifying the output.
       if sort "$nlist" | uniq > "$nlist"T; then
        mv -f "$nlist"T "$nlist"
@@ -4703,6 +4704,12 @@ m4_if([$1], [CXX], [
        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
        _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
         ;;
+      # flang / f18. f95 an alias for gfortran or flang on Debian
+      flang* | f18* | f95*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
       # icc used to be incompatible with GCC.
       # ICC 10 doesn't accept -KPIC any more.
       icc* | ifort*)
index 09ba804..5d1d63f 100644 (file)
@@ -118,7 +118,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -193,7 +193,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -449,9 +449,9 @@ distclean-generic:
 maintainer-clean-generic:
        @echo "This command is intended for maintainers to use"
        @echo "it deletes files that may require special tools to rebuild."
+@ENABLE_PYTHON_BINDINGS_FALSE@uninstall-local:
 @ENABLE_PYTHON_BINDINGS_FALSE@clean-local:
 @ENABLE_PYTHON_BINDINGS_FALSE@install-exec-local:
-@ENABLE_PYTHON_BINDINGS_FALSE@uninstall-local:
 clean: clean-am
 
 clean-am: clean-generic clean-libtool clean-local mostlyclean-am
index 9372bb7..672f81b 100644 (file)
@@ -119,7 +119,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -224,7 +224,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index f33f4bf..12e9e78 100644 (file)
@@ -10,9 +10,9 @@ set_source_files_properties(${cxx_sources} PROPERTIES
 include_directories(
   "${CMAKE_CURRENT_SOURCE_DIR}/includes"
   "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
+  "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/llhttp/include"
 
   ${JEMALLOC_INCLUDE_DIRS}
-  ${SPDYLAY_INCLUDE_DIRS}
   ${LIBXML2_INCLUDE_DIRS}
   ${LIBEV_INCLUDE_DIRS}
   ${OPENSSL_INCLUDE_DIRS}
@@ -25,7 +25,6 @@ include_directories(
 link_libraries(
   nghttp2
   ${JEMALLOC_LIBRARIES}
-  ${SPDYLAY_LIBRARIES}
   ${LIBXML2_LIBRARIES}
   ${LIBEV_LIBRARIES}
   ${OPENSSL_LIBRARIES}
@@ -68,11 +67,6 @@ if(ENABLE_APP)
     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)
@@ -116,11 +110,6 @@ if(ENABLE_APP)
     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
@@ -137,6 +126,12 @@ if(ENABLE_APP)
     shrpx.cc
   )
 
+  if(HAVE_SYSTEMD)
+    target_link_libraries(nghttpx_static ${SYSTEMD_LIBRARIES})
+    target_compile_definitions(nghttpx_static PUBLIC HAVE_LIBSYSTEMD)
+    target_include_directories(nghttpx_static PUBLIC ${SYSTEMD_INCLUDE_DIRS})
+  endif()
+
   if(HAVE_MRUBY)
     target_link_libraries(nghttpx_static mruby-lib)
   endif()
@@ -166,7 +161,8 @@ if(ENABLE_APP)
     )
     add_executable(nghttpx-unittest EXCLUDE_FROM_ALL
       ${NGHTTPX_UNITTEST_SOURCES}
-      $<TARGET_OBJECTS:http-parser>
+      $<TARGET_OBJECTS:llhttp>
+      $<TARGET_OBJECTS:url-parser>
     )
     target_include_directories(nghttpx-unittest PRIVATE ${CUNIT_INCLUDE_DIRS})
     target_compile_definitions(nghttpx-unittest
@@ -184,12 +180,20 @@ if(ENABLE_APP)
     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>)
+  add_executable(nghttp   ${NGHTTP_SOURCES}   $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
+  add_executable(nghttpd  ${NGHTTPD_SOURCES}  $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
+  add_executable(nghttpx  ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
   target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"")
   target_link_libraries(nghttpx nghttpx_static)
-  add_executable(h2load   ${H2LOAD_SOURCES}   $<TARGET_OBJECTS:http-parser>)
+  add_executable(h2load   ${H2LOAD_SOURCES}   $<TARGET_OBJECTS:llhttp>
+    $<TARGET_OBJECTS:url-parser>
+  )
 
   install(TARGETS nghttp nghttpd nghttpx h2load
     RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
@@ -243,7 +247,8 @@ if(ENABLE_ASIO_LIB)
 
   add_library(nghttp2_asio SHARED
      ${NGHTTP2_ASIO_SOURCES}
-     $<TARGET_OBJECTS:http-parser>
+     $<TARGET_OBJECTS:llhttp>
+     $<TARGET_OBJECTS:url-parser>
   )
   target_include_directories(nghttp2_asio PRIVATE
     ${OPENSSL_INCLUDE_DIRS}
@@ -263,7 +268,9 @@ if(ENABLE_ASIO_LIB)
     VERSION 1.0.0 SOVERSION 1)
 
   install(TARGETS nghttp2_asio
-    DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
 
   install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp2_asio.pc"
     DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
index 8dcf580..5075bc9 100644 (file)
@@ -305,7 +305,7 @@ public:
       }
     }
     auto handler =
-        make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
+        std::make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
     if (!ssl) {
       if (handler->connection_made() != 0) {
         return;
@@ -358,11 +358,11 @@ public:
   }
   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));
+    auto rv = fd_cache_.emplace(path, std::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)));
+    auto rv = fd_cache_.insert(
+        std::make_pair(path, std::make_unique<FileEntry>(ent)));
 #endif // !HAVE_STD_MAP_EMPLACE
     auto &res = (*rv).second;
     res->it = rv;
@@ -624,32 +624,30 @@ 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;
+  ssize_t nread;
+  while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR)
+    ;
+  if (nread == -1) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      return write_(*this);
     }
+    return -1;
+  }
+  if (nread == 0) {
+    return -1;
+  }
 
-    if (get_config()->hexdump) {
-      util::hexdump(stdout, buf.data(), nread);
-    }
+  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;
+  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);
@@ -745,39 +743,36 @@ int Http2Handler::read_tls() {
 
   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 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 write_(*this);
+    case SSL_ERROR_WANT_WRITE:
+      // renegotiation started
+      return -1;
+    default:
+      return -1;
     }
+  }
 
-    auto nread = rv;
+  auto nread = rv;
 
-    if (get_config()->hexdump) {
-      util::hexdump(stdout, buf.data(), nread);
-    }
+  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;
+  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);
 }
 
@@ -1023,7 +1018,7 @@ int Http2Handler::submit_push_promise(Stream *stream,
     return promised_stream_id;
   }
 
-  auto promised_stream = make_unique<Stream>(this, promised_stream_id);
+  auto promised_stream = std::make_unique<Stream>(this, promised_stream_id);
 
   auto &promised_header = promised_stream->header;
   promised_header.method = StringRef::from_lit("GET");
@@ -1477,7 +1472,7 @@ int on_begin_headers_callback(nghttp2_session *session,
     return 0;
   }
 
-  auto stream = make_unique<Stream>(hd, frame->hd.stream_id);
+  auto stream = std::make_unique<Stream>(hd, frame->hd.stream_id);
 
   add_stream_read_timeout(stream.get());
 
@@ -1832,10 +1827,10 @@ public:
       if (config_->verbose) {
         std::cerr << "spawning thread #" << i << std::endl;
       }
-      auto worker = make_unique<Worker>();
+      auto worker = std::make_unique<Worker>();
       auto loop = ev_loop_new(get_ev_loop_flags());
-      worker->sessions =
-          make_unique<Sessions>(sv, loop, config_, sessions_->get_ssl_ctx());
+      worker->sessions = std::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);
index 440f6e7..0382164 100644 (file)
@@ -40,6 +40,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/lib \
        -I$(top_srcdir)/src/includes \
        -I$(top_srcdir)/third-party \
+       -I$(top_srcdir)/third-party/llhttp/include \
        @LIBXML2_CFLAGS@ \
        @LIBEV_CFLAGS@ \
        @OPENSSL_CFLAGS@ \
@@ -49,7 +50,8 @@ AM_CPPFLAGS = \
        @DEFS@
 
 LDADD = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la \
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la \
        @JEMALLOC_LIBS@ \
        @LIBXML2_LIBS@ \
        @LIBEV_LIBS@ \
@@ -263,7 +265,8 @@ 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 \
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la \
        @OPENSSL_LIBS@ \
        ${BOOST_LDFLAGS} \
        ${BOOST_ASIO_LIB} \
index 769f9b9..f4d16c9 100644 (file)
@@ -143,7 +143,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -293,7 +293,8 @@ libnghttpx_a_OBJECTS = $(am_libnghttpx_a_OBJECTS)
 am__DEPENDENCIES_1 =
 @ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_DEPENDENCIES =  \
 @ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \
-@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \
+@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/liburl-parser.la \
+@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libllhttp.la \
 @ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \
 @ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \
 @ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \
@@ -366,7 +367,8 @@ am__deflatehd_SOURCES_DIST = deflatehd.cc comp_helper.c comp_helper.h
 deflatehd_OBJECTS = $(am_deflatehd_OBJECTS)
 deflatehd_LDADD = $(LDADD)
 deflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la
 am__h2load_SOURCES_DIST = 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 \
@@ -378,14 +380,16 @@ am__h2load_SOURCES_DIST = util.cc util.h http2.cc http2.h h2load.cc \
 h2load_OBJECTS = $(am_h2load_OBJECTS)
 h2load_LDADD = $(LDADD)
 h2load_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la
 am__inflatehd_SOURCES_DIST = inflatehd.cc comp_helper.c comp_helper.h
 @ENABLE_HPACK_TOOLS_TRUE@am_inflatehd_OBJECTS = inflatehd.$(OBJEXT) \
 @ENABLE_HPACK_TOOLS_TRUE@      $(am__objects_3)
 inflatehd_OBJECTS = $(am_inflatehd_OBJECTS)
 inflatehd_LDADD = $(LDADD)
 inflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la
 am__nghttp_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \
        nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \
        nghttp2_config.h nghttp2_gzip.h network.h nghttp.cc nghttp.h \
@@ -403,7 +407,8 @@ am__objects_5 =
 nghttp_OBJECTS = $(am_nghttp_OBJECTS)
 nghttp_LDADD = $(LDADD)
 nghttp_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la
 am__nghttpd_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \
        nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \
        nghttp2_config.h nghttp2_gzip.h network.h nghttpd.cc tls.cc \
@@ -414,12 +419,14 @@ am__nghttpd_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \
 nghttpd_OBJECTS = $(am_nghttpd_OBJECTS)
 nghttpd_LDADD = $(LDADD)
 nghttpd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la
 am__nghttpx_SOURCES_DIST = shrpx.cc shrpx.h
 @ENABLE_APP_TRUE@am_nghttpx_OBJECTS = nghttpx-shrpx.$(OBJEXT)
 nghttpx_OBJECTS = $(am_nghttpx_OBJECTS)
 am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la
 @ENABLE_APP_TRUE@nghttpx_DEPENDENCIES = libnghttpx.a \
 @ENABLE_APP_TRUE@      $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
 @ENABLE_APP_TRUE@      $(am__append_7)
@@ -901,7 +908,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -1063,6 +1070,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/lib \
        -I$(top_srcdir)/src/includes \
        -I$(top_srcdir)/third-party \
+       -I$(top_srcdir)/third-party/llhttp/include \
        @LIBXML2_CFLAGS@ \
        @LIBEV_CFLAGS@ \
        @OPENSSL_CFLAGS@ \
@@ -1072,7 +1080,8 @@ AM_CPPFLAGS = \
        @DEFS@
 
 LDADD = $(top_builddir)/lib/libnghttp2.la \
-       $(top_builddir)/third-party/libhttp-parser.la \
+       $(top_builddir)/third-party/liburl-parser.la \
+       $(top_builddir)/third-party/libllhttp.la \
        @JEMALLOC_LIBS@ \
        @LIBXML2_LIBS@ \
        @LIBEV_LIBS@ \
@@ -1240,7 +1249,8 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
 @ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
 @ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LIBADD = \
 @ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \
-@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \
+@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/liburl-parser.la \
+@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libllhttp.la \
 @ENABLE_ASIO_LIB_TRUE@ @OPENSSL_LIBS@ \
 @ENABLE_ASIO_LIB_TRUE@ ${BOOST_LDFLAGS} \
 @ENABLE_ASIO_LIB_TRUE@ ${BOOST_ASIO_LIB} \
index fa0de3a..6ad8352 100644 (file)
@@ -32,6 +32,7 @@
 #endif // !_WIN32
 
 #include <cassert>
+#include <utility>
 
 #include "template.h"
 
@@ -65,25 +66,19 @@ struct BlockAllocator {
   ~BlockAllocator() { reset(); }
 
   BlockAllocator(BlockAllocator &&other) noexcept
-      : retain(other.retain),
-        head(other.head),
+      : retain{std::exchange(other.retain, nullptr)},
+        head{std::exchange(other.head, nullptr)},
         block_size(other.block_size),
-        isolation_threshold(other.isolation_threshold) {
-    other.retain = nullptr;
-    other.head = nullptr;
-  }
+        isolation_threshold(other.isolation_threshold) {}
 
   BlockAllocator &operator=(BlockAllocator &&other) noexcept {
     reset();
 
-    retain = other.retain;
-    head = other.head;
+    retain = std::exchange(other.retain, nullptr);
+    head = std::exchange(other.head, nullptr);
     block_size = other.block_size;
     isolation_threshold = other.isolation_threshold;
 
-    other.retain = nullptr;
-    other.head = nullptr;
-
     return *this;
   }
 
index 1fa93ca..a81e213 100644 (file)
@@ -34,7 +34,7 @@ namespace nghttp2 {
 namespace asio_http2 {
 namespace client {
 
-request::request() : impl_(make_unique<request_impl>()) {}
+request::request() : impl_(std::make_unique<request_impl>()) {}
 
 request::~request() {}
 
index 4805422..56b8072 100644 (file)
@@ -34,7 +34,7 @@ namespace nghttp2 {
 namespace asio_http2 {
 namespace client {
 
-response::response() : impl_(make_unique<response_impl>()) {}
+response::response() : impl_(std::make_unique<response_impl>()) {}
 
 response::~response() {}
 
index a5acffc..5cf01c7 100644 (file)
@@ -154,7 +154,7 @@ const nghttp2_priority_spec *priority_spec::get() const {
   return &spec_;
 }
 
-const bool priority_spec::valid() const { return valid_; }
+bool priority_spec::valid() const { return valid_; }
 
 } // namespace client
 } // namespace asio_http2
index 16f91fa..b96824d 100644 (file)
@@ -473,7 +473,7 @@ stream *session_impl::create_push_stream(int32_t stream_id) {
 }
 
 std::unique_ptr<stream> session_impl::create_stream() {
-  return make_unique<stream>(this);
+  return std::make_unique<stream>(this);
 }
 
 const request *session_impl::submit(boost::system::error_code &ec,
index 33db41c..74c9227 100644 (file)
@@ -200,6 +200,15 @@ server::io_services() const {
   return io_service_pool_.io_services();
 }
 
+const std::vector<int> server::ports() const {
+  auto ports = std::vector<int>(acceptors_.size());
+  auto index = 0;
+  for (const auto &acceptor : acceptors_) {
+    ports[index++] = acceptor.local_endpoint().port();
+  }
+  return ports;
+}
+
 } // namespace server
 } // namespace asio_http2
 } // namespace nghttp2
index ba84034..1190e32 100644 (file)
@@ -79,6 +79,9 @@ public:
   const std::vector<std::shared_ptr<boost::asio::io_service>> &
   io_services() const;
 
+  /// Returns a vector with all the acceptors ports in use.
+  const std::vector<int> ports() const;
+
 private:
   /// Initiate an asynchronous accept operation.
   void start_accept(tcp::acceptor &acceptor, serve_mux &mux);
index 6be7940..daf9a66 100644 (file)
 #include "util.h"
 #include "template.h"
 
+#if BOOST_VERSION >= 107000
+#  define GET_IO_SERVICE(s)                                                    \
+    ((boost::asio::io_context &)(s).get_executor().context())
+#else
+#  define GET_IO_SERVICE(s) ((s).get_io_service())
+#endif
+
 namespace nghttp2 {
 
 namespace asio_http2 {
@@ -71,7 +78,7 @@ public:
       SocketArgs &&... args)
       : socket_(std::forward<SocketArgs>(args)...),
         mux_(mux),
-        deadline_(socket_.get_io_service()),
+        deadline_(GET_IO_SERVICE(socket_)),
         tls_handshake_timeout_(tls_handshake_timeout),
         read_timeout_(read_timeout),
         writing_(false),
@@ -82,7 +89,7 @@ public:
     boost::system::error_code ec;
 
     handler_ = std::make_shared<http2_handler>(
-        socket_.get_io_service(), socket_.lowest_layer().remote_endpoint(ec),
+        GET_IO_SERVICE(socket_), socket_.lowest_layer().remote_endpoint(ec),
         [this]() { do_write(); }, mux_);
     if (handler_->start() != 0) {
       stop();
index 997fd40..02d3d19 100644 (file)
@@ -36,7 +36,7 @@ namespace asio_http2 {
 
 namespace server {
 
-http2::http2() : impl_(make_unique<http2_impl>()) {}
+http2::http2() : impl_(std::make_unique<http2_impl>()) {}
 
 http2::~http2() {}
 
@@ -90,6 +90,8 @@ http2::io_services() const {
   return impl_->io_services();
 }
 
+std::vector<int> http2::ports() const { return impl_->ports(); }
+
 } // namespace server
 
 } // namespace asio_http2
index 5a784ed..c1fc195 100644 (file)
@@ -305,7 +305,8 @@ int http2_handler::start() {
 }
 
 stream *http2_handler::create_stream(int32_t stream_id) {
-  auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
+  auto p =
+      streams_.emplace(stream_id, std::make_unique<stream>(this, stream_id));
   assert(p.second);
   return (*p.first).second.get();
 }
index 83368d4..00afdd6 100644 (file)
@@ -78,6 +78,8 @@ http2_impl::io_services() const {
   return server_->io_services();
 }
 
+std::vector<int> http2_impl::ports() const { return server_->ports(); }
+
 } // namespace server
 
 } // namespace asio_http2
index b55b68c..93a6d2c 100644 (file)
@@ -54,6 +54,7 @@ public:
   void join();
   const std::vector<std::shared_ptr<boost::asio::io_service>> &
   io_services() const;
+  std::vector<int> ports() const;
 
 private:
   std::unique_ptr<server> server_;
index 9612363..36669a5 100644 (file)
@@ -34,7 +34,7 @@ namespace nghttp2 {
 namespace asio_http2 {
 namespace server {
 
-request::request() : impl_(make_unique<request_impl>()) {}
+request::request() : impl_(std::make_unique<request_impl>()) {}
 
 request::~request() {}
 
index f420693..f67921a 100644 (file)
@@ -34,7 +34,7 @@ namespace nghttp2 {
 namespace asio_http2 {
 namespace server {
 
-response::response() : impl_(make_unique<response_impl>()) {}
+response::response() : impl_(std::make_unique<response_impl>()) {}
 
 response::~response() {}
 
index 9e2592a..334b1e2 100644 (file)
@@ -48,7 +48,7 @@
 
 #include <openssl/err.h>
 
-#include "http-parser/http_parser.h"
+#include "url-parser/url_parser.h"
 
 #include "h2load_http1_session.h"
 #include "h2load_http2_session.h"
@@ -91,6 +91,7 @@ Config::Config()
       header_table_size(4_k),
       encoder_header_table_size(4_k),
       data_fd(-1),
+      log_fd(-1),
       port(0),
       default_port(0),
       verbose(false),
@@ -211,7 +212,7 @@ void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
       --worker->nreqs_rem;
     }
     auto client =
-        make_unique<Client>(worker->next_client_id++, worker, req_todo);
+        std::make_unique<Client>(worker->next_client_id++, worker, req_todo);
 
     ++worker->nconns_made;
 
@@ -748,6 +749,7 @@ void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
       }
     }
 
+    stream.req_stat.status = status;
     if (status >= 200 && status < 300) {
       ++worker->stats.status[2];
       stream.status_success = 1;
@@ -775,6 +777,7 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
     return;
   }
 
+  stream.req_stat.status = status;
   if (status >= 200 && status < 300) {
     ++worker->stats.status[2];
     stream.status_success = 1;
@@ -821,6 +824,33 @@ void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
     }
     ++worker->stats.req_done;
     ++req_done;
+
+    if (worker->config->log_fd != -1) {
+      auto start = std::chrono::duration_cast<std::chrono::microseconds>(
+          req_stat->request_wall_time.time_since_epoch());
+      auto delta = std::chrono::duration_cast<std::chrono::microseconds>(
+          req_stat->stream_close_time - req_stat->request_time);
+
+      std::array<uint8_t, 256> buf;
+      auto p = std::begin(buf);
+      p = util::utos(p, start.count());
+      *p++ = '\t';
+      if (success) {
+        p = util::utos(p, req_stat->status);
+      } else {
+        *p++ = '-';
+        *p++ = '1';
+      }
+      *p++ = '\t';
+      p = util::utos(p, delta.count());
+      *p++ = '\n';
+
+      auto nwrite = static_cast<size_t>(std::distance(std::begin(buf), p));
+      assert(nwrite <= buf.size());
+      while (write(worker->config->log_fd, buf.data(), nwrite) == -1 &&
+             errno == EINTR)
+        ;
+    }
   }
 
   worker->report_progress();
@@ -869,9 +899,9 @@ int Client::connection_made() {
     if (next_proto) {
       auto proto = StringRef{next_proto, next_proto_len};
       if (util::check_h2_is_selected(proto)) {
-        session = make_unique<Http2Session>(this);
+        session = std::make_unique<Http2Session>(this);
       } else if (util::streq(NGHTTP2_H1_1, proto)) {
-        session = make_unique<Http1Session>(this);
+        session = std::make_unique<Http1Session>(this);
       }
 
       // Just assign next_proto to selected_proto anyway to show the
@@ -886,7 +916,7 @@ int Client::connection_made() {
           std::cout
               << "Server does not support NPN/ALPN. Falling back to HTTP/1.1."
               << std::endl;
-          session = make_unique<Http1Session>(this);
+          session = std::make_unique<Http1Session>(this);
           selected_proto = NGHTTP2_H1_1.str();
           break;
         }
@@ -910,11 +940,11 @@ int Client::connection_made() {
   } else {
     switch (config.no_tls_proto) {
     case Config::PROTO_HTTP2:
-      session = make_unique<Http2Session>(this);
+      session = std::make_unique<Http2Session>(this);
       selected_proto = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;
       break;
     case Config::PROTO_HTTP1_1:
-      session = make_unique<Http1Session>(this);
+      session = std::make_unique<Http1Session>(this);
       selected_proto = NGHTTP2_H1_1.str();
       break;
     default:
@@ -1177,6 +1207,7 @@ int Client::write_tls() {
 
 void Client::record_request_time(RequestStat *req_stat) {
   req_stat->request_time = std::chrono::steady_clock::now();
+  req_stat->request_wall_time = std::chrono::system_clock::now();
 }
 
 void Client::record_connect_start_time() {
@@ -1319,7 +1350,7 @@ void Worker::run() {
         --nreqs_rem;
       }
 
-      auto client = make_unique<Client>(next_client_id++, this, req_todo);
+      auto client = std::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();
@@ -1513,7 +1544,7 @@ process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
 namespace {
 void resolve_host() {
   if (config.base_uri_unix) {
-    auto res = make_unique<addrinfo>();
+    auto res = std::make_unique<addrinfo>();
     res->ai_family = config.unix_addr.sun_family;
     res->ai_socktype = SOCK_STREAM;
     res->ai_addrlen = sizeof(config.unix_addr);
@@ -1722,13 +1753,13 @@ std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
   }
 
   if (config.is_rate_mode()) {
-    return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
-                               &config);
+    return std::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);
+    return std::make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
+                                    max_samples, &config);
   }
 }
 } // namespace
@@ -1940,6 +1971,14 @@ Options:
               this option value and the value which server specified.
               Default: )"
       << util::utos_unit(config.encoder_header_table_size) << R"(
+  --log-file=<PATH>
+              Write per-request information to a file as tab-separated
+              columns: start  time as  microseconds since  epoch; HTTP
+              status code;  microseconds until end of  response.  More
+              columns may be added later.  Rows are ordered by end-of-
+              response  time when  using  one worker  thread, but  may
+              appear slightly  out of order with  multiple threads due
+              to buffering.  Status code is -1 for failed streams.
   -v, --verbose
               Output debug information.
   --version   Display version information and exit.
@@ -1966,6 +2005,7 @@ int main(int argc, char **argv) {
 #endif // NOTHREADS
 
   std::string datafile;
+  std::string logfile;
   bool nreqs_set_manually = false;
   while (1) {
     static int flag = 0;
@@ -1996,6 +2036,7 @@ int main(int argc, char **argv) {
         {"header-table-size", required_argument, &flag, 7},
         {"encoder-header-table-size", required_argument, &flag, 8},
         {"warm-up-time", required_argument, &flag, 9},
+        {"log-file", required_argument, &flag, 10},
         {nullptr, 0, nullptr, 0}};
     int option_index = 0;
     auto c = getopt_long(argc, argv,
@@ -2219,6 +2260,10 @@ int main(int argc, char **argv) {
           exit(EXIT_FAILURE);
         }
         break;
+      case 10:
+        // --log-file
+        logfile = optarg;
+        break;
       }
       break;
     default:
@@ -2381,6 +2426,15 @@ int main(int argc, char **argv) {
     config.data_length = data_stat.st_size;
   }
 
+  if (!logfile.empty()) {
+    config.log_fd = open(logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND,
+                         S_IRUSR | S_IWUSR | S_IRGRP);
+    if (config.log_fd == -1) {
+      std::cerr << "--log-file: Could not open file " << logfile << std::endl;
+      exit(EXIT_FAILURE);
+    }
+  }
+
   struct sigaction act {};
   act.sa_handler = SIG_IGN;
   sigaction(SIGPIPE, &act, nullptr);
@@ -2731,6 +2785,10 @@ time for request: )"
 
   SSL_CTX_free(ssl_ctx);
 
+  if (config.log_fd != -1) {
+    close(config.log_fd);
+  }
+
   return 0;
 }
 
index bde36e8..a5de461 100644 (file)
@@ -97,6 +97,8 @@ struct Config {
   uint32_t encoder_header_table_size;
   // file descriptor for upload data
   int data_fd;
+  // file descriptor to write per-request stats to.
+  int log_fd;
   uint16_t port;
   uint16_t default_port;
   bool verbose;
@@ -122,10 +124,14 @@ struct Config {
 struct RequestStat {
   // time point when request was sent
   std::chrono::steady_clock::time_point request_time;
+  // same, but in wall clock reference frame
+  std::chrono::system_clock::time_point request_wall_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;
+  // HTTP status code
+  int status;
   // 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;
index 645e11a..14e4d58 100644 (file)
 #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) {
+int htp_msg_begincb(llhttp_t *htp) {
   auto session = static_cast<Http1Session *>(htp->data);
 
   if (session->stream_resp_counter_ > session->stream_req_counter_) {
@@ -67,9 +53,14 @@ int htp_msg_begincb(http_parser *htp) {
 
 namespace {
 // HTTP response status code
-int htp_statuscb(http_parser *htp, const char *at, size_t length) {
+int htp_statuscb(llhttp_t *htp, const char *at, size_t length) {
   auto session = static_cast<Http1Session *>(htp->data);
   auto client = session->get_client();
+
+  if (htp->status_code / 100 == 1) {
+    return 0;
+  }
+
   client->on_status_code(session->stream_resp_counter_, htp->status_code);
 
   return 0;
@@ -78,11 +69,15 @@ int htp_statuscb(http_parser *htp, const char *at, size_t length) {
 
 namespace {
 // HTTP response message complete
-int htp_msg_completecb(http_parser *htp) {
+int htp_msg_completecb(llhttp_t *htp) {
   auto session = static_cast<Http1Session *>(htp->data);
   auto client = session->get_client();
 
-  client->final = http_should_keep_alive(htp) == 0;
+  if (htp->status_code / 100 == 1) {
+    return 0;
+  }
+
+  client->final = llhttp_should_keep_alive(htp) == 0;
   auto req_stat = client->get_req_stat(session->stream_resp_counter_);
 
   assert(req_stat);
@@ -97,14 +92,13 @@ int htp_msg_completecb(http_parser *htp) {
   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 HPE_PAUSED;
   }
 
   return 0;
@@ -112,7 +106,7 @@ int htp_msg_completecb(http_parser *htp) {
 } // namespace
 
 namespace {
-int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
+int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) {
   auto session = static_cast<Http1Session *>(htp->data);
   auto client = session->get_client();
 
@@ -123,7 +117,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
 } // namespace
 
 namespace {
-int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
+int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) {
   auto session = static_cast<Http1Session *>(htp->data);
   auto client = session->get_client();
 
@@ -134,7 +128,13 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
 } // namespace
 
 namespace {
-int htp_body_cb(http_parser *htp, const char *data, size_t len) {
+int htp_hdrs_completecb(llhttp_t *htp) {
+  return !http2::expect_response_body(htp->status_code);
+}
+} // namespace
+
+namespace {
+int htp_body_cb(llhttp_t *htp, const char *data, size_t len) {
   auto session = static_cast<Http1Session *>(htp->data);
   auto client = session->get_client();
 
@@ -146,18 +146,32 @@ int htp_body_cb(http_parser *htp, const char *data, size_t len) {
 } // 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;
+constexpr llhttp_settings_t htp_hooks = {
+    htp_msg_begincb,     // llhttp_cb      on_message_begin;
+    nullptr,             // llhttp_data_cb on_url;
+    htp_statuscb,        // llhttp_data_cb on_status;
+    htp_hdr_keycb,       // llhttp_data_cb on_header_field;
+    htp_hdr_valcb,       // llhttp_data_cb on_header_value;
+    htp_hdrs_completecb, // llhttp_cb      on_headers_complete;
+    htp_body_cb,         // llhttp_data_cb on_body;
+    htp_msg_completecb,  // llhttp_cb      on_message_complete;
+    nullptr,             // llhttp_cb      on_chunk_header
+    nullptr,             // llhttp_cb      on_chunk_complete
 };
 } // namespace
 
+Http1Session::Http1Session(Client *client)
+    : stream_req_counter_(1),
+      stream_resp_counter_(1),
+      client_(client),
+      htp_(),
+      complete_(false) {
+  llhttp_init(&htp_, HTTP_RESPONSE, &htp_hooks);
+  htp_.data = this;
+}
+
+Http1Session::~Http1Session() {}
+
 void Http1Session::on_connect() { client_->signal_write(); }
 
 int Http1Session::submit_request() {
@@ -187,15 +201,18 @@ int Http1Session::submit_request() {
 }
 
 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);
+  auto htperr =
+      llhttp_execute(&htp_, reinterpret_cast<const char *>(data), len);
+  auto nread = htperr == HPE_OK
+                   ? len
+                   : static_cast<size_t>(reinterpret_cast<const uint8_t *>(
+                                             llhttp_get_error_pos(&htp_)) -
+                                         data);
 
   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;
@@ -203,8 +220,8 @@ int Http1Session::on_read(const uint8_t *data, size_t len) {
 
   if (htperr != HPE_OK) {
     std::cerr << "[ERROR] HTTP parse error: "
-              << "(" << http_errno_name(htperr) << ") "
-              << http_errno_description(htperr) << std::endl;
+              << "(" << llhttp_errno_name(htperr) << ") "
+              << llhttp_get_error_reason(&htp_) << std::endl;
     return -1;
   }
 
index 3a0b5db..cc10f50 100644 (file)
@@ -29,6 +29,8 @@
 
 #include <nghttp2/nghttp2.h>
 
+#include "llhttp.h"
+
 namespace h2load {
 
 struct Client;
@@ -49,7 +51,7 @@ public:
 
 private:
   Client *client_;
-  http_parser htp_;
+  llhttp_t htp_;
   bool complete_;
 };
 
index 566cfb0..c5b596f 100644 (file)
@@ -24,6 +24,8 @@
  */
 #include "http2.h"
 
+#include "llhttp.h"
+
 #include "util.h"
 
 namespace nghttp2 {
@@ -503,6 +505,11 @@ void build_http1_headers_from_headers(DefaultMemchunks *buf,
         continue;
       }
       break;
+    case HD_TRANSFER_ENCODING:
+      if (flags & HDOP_STRIP_TRANSFER_ENCODING) {
+        continue;
+      }
+      break;
     case HD_FORWARDED:
       if (flags & HDOP_STRIP_FORWARDED) {
         continue;
@@ -1381,6 +1388,11 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
   switch (namelen) {
   case 3:
     switch (name[2]) {
+    case 'L':
+      if (util::streq_l("AC", name, 2)) {
+        return HTTP_ACL;
+      }
+      break;
     case 'T':
       if (util::streq_l("GE", name, 2)) {
         return HTTP_GET;
@@ -1394,6 +1406,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
   case 4:
     switch (name[3]) {
     case 'D':
+      if (util::streq_l("BIN", name, 3)) {
+        return HTTP_BIND;
+      }
       if (util::streq_l("HEA", name, 3)) {
         return HTTP_HEAD;
       }
@@ -1404,6 +1419,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
       }
       break;
     case 'K':
+      if (util::streq_l("LIN", name, 3)) {
+        return HTTP_LINK;
+      }
       if (util::streq_l("LOC", name, 3)) {
         return HTTP_LOCK;
       }
@@ -1447,10 +1465,21 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
     break;
   case 6:
     switch (name[5]) {
+    case 'D':
+      if (util::streq_l("REBIN", name, 5)) {
+        return HTTP_REBIND;
+      }
+      if (util::streq_l("UNBIN", name, 5)) {
+        return HTTP_UNBIND;
+      }
+      break;
     case 'E':
       if (util::streq_l("DELET", name, 5)) {
         return HTTP_DELETE;
       }
+      if (util::streq_l("SOURC", name, 5)) {
+        return HTTP_SOURCE;
+      }
       break;
     case 'H':
       if (util::streq_l("SEARC", name, 5)) {
@@ -1458,6 +1487,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
       }
       break;
     case 'K':
+      if (util::streq_l("UNLIN", name, 5)) {
+        return HTTP_UNLINK;
+      }
       if (util::streq_l("UNLOC", name, 5)) {
         return HTTP_UNLOCK;
       }
@@ -1549,8 +1581,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
 }
 
 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))};
+  // we happened to use same value for method with llhttp.
+  return StringRef{
+      llhttp_method_name(static_cast<llhttp_method>(method_token))};
 }
 
 StringRef get_pure_path_component(const StringRef &uri) {
@@ -1576,6 +1609,10 @@ int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
   int rv;
   StringRef rel, relq;
 
+  if (uri.size() == 0) {
+    return -1;
+  }
+
   http_parser_url u{};
 
   rv = http_parser_parse_url(uri.c_str(), uri.size(), 0, &u);
@@ -1874,6 +1911,10 @@ StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key) {
   return StringRef{dest, end};
 }
 
+bool legacy_http1(int major, int minor) {
+  return major <= 0 || (major == 1 && minor == 0);
+}
+
 } // namespace http2
 
 } // namespace nghttp2
index 9e7d749..b0b1065 100644 (file)
@@ -35,7 +35,7 @@
 
 #include <nghttp2/nghttp2.h>
 
-#include "http-parser/http_parser.h"
+#include "url-parser/url_parser.h"
 
 #include "util.h"
 #include "memchunk.h"
@@ -217,6 +217,9 @@ enum HeaderBuildOp {
   // Sec-WebSocket-Key header field must be stripped.  If this flag is
   // not set, all Sec-WebSocket-Key header fields are added.
   HDOP_STRIP_SEC_WEBSOCKET_KEY = 1 << 6,
+  // Transfer-Encoding header field must be stripped.  If this flag is
+  // not set, all Transfer-Encoding header fields are added.
+  HDOP_STRIP_TRANSFER_ENCODING = 1 << 7,
 };
 
 // Appends headers in |headers| to |nv|.  |headers| must be indexed
@@ -393,15 +396,15 @@ bool expect_response_body(int method_token, int status_code);
 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.
+// Only methods defined in llhttp.h (llhttp_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.
+// Returns string representation of |method_token|.  This is wrapper
+// around llhttp_method_name from llhttp.  If |method_token| is
+// unknown, program aborts.  The returned StringRef is guaranteed to
+// be NULL-terminated.
 StringRef to_method_string(int method_token);
 
 StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
@@ -437,6 +440,10 @@ bool contains_trailers(const StringRef &s);
 // of error.
 StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key);
 
+// Returns true if HTTP version represents pre-HTTP/1.1 (e.g.,
+// HTTP/0.9 or HTTP/1.0).
+bool legacy_http1(int major, int minor);
+
 } // namespace http2
 
 } // namespace nghttp2
index 04c2611..b998ee7 100644 (file)
@@ -30,7 +30,7 @@
 
 #include <CUnit/CUnit.h>
 
-#include "http-parser/http_parser.h"
+#include "url-parser/url_parser.h"
 
 #include "http2.h"
 #include "util.h"
@@ -995,12 +995,8 @@ void test_http2_construct_push_component(void) {
 
   uri = StringRef{};
 
-  CU_ASSERT(0 == http2::construct_push_component(balloc, scheme, authority,
-                                                 path, base, uri));
-  CU_ASSERT("" == scheme);
-  CU_ASSERT("" == authority);
-  CU_ASSERT("/" == path);
-
+  CU_ASSERT(-1 == http2::construct_push_component(balloc, scheme, authority,
+                                                  path, base, uri));
   scheme = StringRef{};
   authority = StringRef{};
   path = StringRef{};
index 6424663..3b63438 100644 (file)
@@ -119,7 +119,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -244,7 +244,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index 7b256a7..59ba9b2 100644 (file)
@@ -133,7 +133,7 @@ public:
 
   // Indicates whether or not this spec is valid (i.e. was constructed with
   // values).
-  const bool valid() const;
+  bool valid() const;
 
 private:
   nghttp2_priority_spec spec_;
index 5818e30..d4ec489 100644 (file)
@@ -142,8 +142,8 @@ public:
   // 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.
+  // immediately, and the caller should call stop() and 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,
@@ -214,6 +214,9 @@ public:
   const std::vector<std::shared_ptr<boost::asio::io_service>> &
   io_services() const;
 
+  // Returns a vector with the ports in use
+  std::vector<int> ports() const;
+
 private:
   std::unique_ptr<http2_impl> impl_;
 };
index d373d06..f0e24b8 100644 (file)
@@ -44,6 +44,7 @@ struct iovec {
 #include <array>
 #include <algorithm>
 #include <string>
+#include <utility>
 
 #include "template.h"
 
@@ -111,11 +112,10 @@ template <typename Memchunk> struct Memchunks {
       : 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;
-  }
+      : pool{other.pool}, // keep other.pool
+        head{std::exchange(other.head, nullptr)},
+        tail{std::exchange(other.tail, nullptr)},
+        len{std::exchange(other.len, 0)} {}
   Memchunks &operator=(const Memchunks &) = delete;
   Memchunks &operator=(Memchunks &&other) noexcept {
     if (this == &other) {
@@ -125,12 +125,9 @@ template <typename Memchunk> struct Memchunks {
     reset();
 
     pool = other.pool;
-    head = other.head;
-    tail = other.tail;
-    len = other.len;
-
-    other.head = other.tail = nullptr;
-    other.len = 0;
+    head = std::exchange(other.head, nullptr);
+    tail = std::exchange(other.tail, nullptr);
+    len = std::exchange(other.len, 0);
 
     return *this;
   }
@@ -190,6 +187,14 @@ template <typename Memchunk> struct Memchunks {
   size_t append(const ImmutableString &s) {
     return append(s.c_str(), s.size());
   }
+  size_t copy(Memchunks &dest) {
+    auto m = head;
+    while (m) {
+      dest.append(m->pos, m->len());
+      m = m->next;
+    }
+    return len;
+  }
   size_t remove(void *dest, size_t count) {
     if (!tail || count == 0) {
       return 0;
@@ -335,14 +340,12 @@ template <typename Memchunk> struct PeekMemchunks {
         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();
-  }
+      : memchunks{std::move(other.memchunks)},
+        cur{std::exchange(other.cur, nullptr)},
+        cur_pos{std::exchange(other.cur_pos, nullptr)},
+        cur_last{std::exchange(other.cur_last, nullptr)},
+        len{std::exchange(other.len, 0)},
+        peeking{std::exchange(other.peeking, true)} {}
   PeekMemchunks &operator=(const PeekMemchunks &) = delete;
   PeekMemchunks &operator=(PeekMemchunks &&other) noexcept {
     if (this == &other) {
@@ -350,13 +353,11 @@ template <typename Memchunk> struct PeekMemchunks {
     }
 
     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();
+    cur = std::exchange(other.cur, nullptr);
+    cur_pos = std::exchange(other.cur_pos, nullptr);
+    cur_last = std::exchange(other.cur_last, nullptr);
+    len = std::exchange(other.len, 0);
+    peeking = std::exchange(other.peeking, true);
 
     return *this;
   }
index 41854b7..fe0a892 100644 (file)
@@ -224,10 +224,10 @@ void test_peek_memchunks_append(void) {
   MemchunkPool16 pool;
   PeekMemchunks16 pchunks(&pool);
 
-  std::array<uint8_t, 32> b{{
+  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());
@@ -259,10 +259,10 @@ void test_peek_memchunks_disable_peek_drain(void) {
   MemchunkPool16 pool;
   PeekMemchunks16 pchunks(&pool);
 
-  std::array<uint8_t, 32> b{{
+  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());
@@ -287,10 +287,10 @@ void test_peek_memchunks_disable_peek_no_drain(void) {
   MemchunkPool16 pool;
   PeekMemchunks16 pchunks(&pool);
 
-  std::array<uint8_t, 32> b{{
+  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());
@@ -315,10 +315,10 @@ void test_peek_memchunks_reset(void) {
   MemchunkPool16 pool;
   PeekMemchunks16 pchunks(&pool);
 
-  std::array<uint8_t, 32> b{{
+  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());
index 7aaa65b..81e7ebe 100644 (file)
@@ -233,7 +233,7 @@ void Request::init_html_parser() {
     base_uri += util::get_uri_field(uri.c_str(), u, UF_QUERY);
   }
 
-  html_parser = make_unique<HtmlParser>(base_uri);
+  html_parser = std::make_unique<HtmlParser>(base_uri);
 }
 
 int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
@@ -401,7 +401,7 @@ void ContinueTimer::dispatch_continue() {
 }
 
 namespace {
-int htp_msg_begincb(http_parser *htp) {
+int htp_msg_begincb(llhttp_t *htp) {
   if (config.verbose) {
     print_timer();
     std::cout << " HTTP Upgrade response" << std::endl;
@@ -411,7 +411,7 @@ int htp_msg_begincb(http_parser *htp) {
 } // namespace
 
 namespace {
-int htp_msg_completecb(http_parser *htp) {
+int htp_msg_completecb(llhttp_t *htp) {
   auto client = static_cast<HttpClient *>(htp->data);
   client->upgrade_response_status_code = htp->status_code;
   client->upgrade_response_complete = true;
@@ -420,15 +420,17 @@ int htp_msg_completecb(http_parser *htp) {
 } // 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;
+constexpr llhttp_settings_t htp_hooks = {
+    htp_msg_begincb,    // llhttp_cb      on_message_begin;
+    nullptr,            // llhttp_data_cb on_url;
+    nullptr,            // llhttp_data_cb on_status;
+    nullptr,            // llhttp_data_cb on_header_field;
+    nullptr,            // llhttp_data_cb on_header_value;
+    nullptr,            // llhttp_cb      on_headers_complete;
+    nullptr,            // llhttp_data_cb on_body;
+    htp_msg_completecb, // llhttp_cb      on_message_complete;
+    nullptr,            // llhttp_cb      on_chunk_header
+    nullptr,            // llhttp_cb      on_chunk_complete
 };
 } // namespace
 
@@ -527,7 +529,7 @@ int submit_request(HttpClient *client, const Headers &headers, Request *req) {
   req->req_nva = std::move(build_headers);
 
   if (expect_continue) {
-    auto timer = make_unique<ContinueTimer>(client->loop, req);
+    auto timer = std::make_unique<ContinueTimer>(client->loop, req);
     req->continue_timer = std::move(timer);
   }
 
@@ -885,8 +887,8 @@ int HttpClient::connected() {
   writefn = &HttpClient::write_clear;
 
   if (need_upgrade()) {
-    htp = make_unique<http_parser>();
-    http_parser_init(htp.get(), HTTP_RESPONSE);
+    htp = std::make_unique<llhttp_t>();
+    llhttp_init(htp.get(), HTTP_RESPONSE, &htp_hooks);
     htp->data = this;
 
     return do_write();
@@ -1031,19 +1033,22 @@ int HttpClient::on_upgrade_connect() {
 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);
+  auto htperr =
+      llhttp_execute(htp.get(), reinterpret_cast<const char *>(data), len);
+  auto nread = htperr == HPE_OK
+                   ? len
+                   : static_cast<size_t>(reinterpret_cast<const uint8_t *>(
+                                             llhttp_get_error_pos(htp.get())) -
+                                         data);
 
   if (config.verbose) {
     std::cout.write(reinterpret_cast<const char *>(data), nread);
   }
 
-  auto htperr = HTTP_PARSER_ERRNO(htp.get());
-
-  if (htperr != HPE_OK) {
+  if (htperr != HPE_OK && htperr != HPE_PAUSED_UPGRADE) {
     std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: "
-              << "(" << http_errno_name(htperr) << ") "
-              << http_errno_description(htperr) << std::endl;
+              << "(" << llhttp_errno_name(htperr) << ") "
+              << llhttp_get_error_reason(htp.get()) << std::endl;
     return -1;
   }
 
@@ -1453,8 +1458,8 @@ bool HttpClient::add_request(const std::string &uri,
     path_cache.insert(uri);
   }
 
-  reqvec.push_back(
-      make_unique<Request>(uri, u, data_prd, data_length, pri_spec, level));
+  reqvec.push_back(std::make_unique<Request>(uri, u, data_prd, data_length,
+                                             pri_spec, level));
   return true;
 }
 
@@ -1854,7 +1859,7 @@ int on_begin_headers_callback(nghttp2_session *session,
 
     nghttp2_priority_spec_default_init(&pri_spec);
 
-    auto req = make_unique<Request>("", u, nullptr, 0, pri_spec);
+    auto req = std::make_unique<Request>("", u, nullptr, 0, pri_spec);
     req->stream_id = stream_id;
 
     nghttp2_session_set_stream_user_data(session, stream_id, req.get());
index 120eb74..bec8a65 100644 (file)
@@ -47,7 +47,7 @@
 
 #include <nghttp2/nghttp2.h>
 
-#include "http-parser/http_parser.h"
+#include "llhttp.h"
 
 #include "memchunk.h"
 #include "http2.h"
@@ -265,7 +265,7 @@ struct HttpClient {
   std::string host;
   std::string hostport;
   // Used for parse the HTTP upgrade response from server
-  std::unique_ptr<http_parser> htp;
+  std::unique_ptr<llhttp_t> htp;
   SessionTiming timing;
   ev_io wev;
   ev_io rev;
index efa0110..e223240 100644 (file)
@@ -435,7 +435,7 @@ int main(int argc, char **argv) {
 #ifdef __sgi
     if (daemon(0, 0, 0, 0) == -1) {
 #else
-    if (daemon(0, 0) == -1) {
+    if (util::daemonize(0, 0) == -1) {
 #endif
       perror("daemon");
       exit(EXIT_FAILURE);
index 5809deb..2dd0493 100644 (file)
@@ -305,7 +305,7 @@ int save_pid() {
   auto &pid_file = config->pid_file;
 
   auto len = config->pid_file.size() + SUFFIX.size();
-  auto buf = make_unique<char[]>(len + 1);
+  auto buf = std::make_unique<char[]>(len + 1);
   auto p = buf.get();
 
   p = std::copy(std::begin(pid_file), std::end(pid_file), p);
@@ -438,7 +438,7 @@ void exec_binary() {
     nghttp2_Exit(EXIT_FAILURE);
   }
 
-  auto argv = make_unique<char *[]>(suconfig.argc + 1);
+  auto argv = std::make_unique<char *[]>(suconfig.argc + 1);
 
   argv[0] = exec_path;
   for (int i = 1; i < suconfig.argc; ++i) {
@@ -454,7 +454,8 @@ void exec_binary() {
   auto &listenerconf = config->conn.listener;
 
   // 2 for ENV_ORIG_PID and terminal nullptr.
-  auto envp = make_unique<char *[]>(envlen + listenerconf.addrs.size() + 2);
+  auto envp =
+      std::make_unique<char *[]>(envlen + listenerconf.addrs.size() + 2);
   size_t envidx = 0;
 
   std::vector<ImmutableString> fd_envs;
@@ -1147,7 +1148,7 @@ int call_daemon() {
     return 0;
   }
 #  endif // HAVE_LIBSYSTEMD
-  return daemon(0, 0);
+  return util::daemonize(0, 0);
 #endif   // !__sgi
 }
 } // namespace
@@ -1354,7 +1355,7 @@ int event_loop() {
     return -1;
   }
 
-  worker_process_add(make_unique<WorkerProcess>(loop, pid, ipc_fd));
+  worker_process_add(std::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
@@ -1403,10 +1404,10 @@ constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
 } // 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")");
+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 {
@@ -1558,6 +1559,7 @@ void fill_default_config(Config *config) {
   }
 
   loggingconf.syslog_facility = LOG_DAEMON;
+  loggingconf.severity = NOTICE;
 
   auto &connconf = config->conn;
   {
@@ -1737,13 +1739,13 @@ Connections:
               "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
               "affinity=<METHOD>",    "dns",    "redirect-if-not-tls",
               "upgrade-scheme",                        "mruby=<PATH>",
-              "read-timeout=<DURATION>",                           and
-              "write-timeout=<DURATION>".   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.
+              "read-timeout=<DURATION>",   "write-timeout=<DURATION>",
+              "group=<GROUP>",  "group-weight=<N>", and  "weight=<N>".
+              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
@@ -1849,6 +1851,31 @@ Connections:
               pattern,            --backend-read-timeout           and
               --backend-write-timeout are used.
 
+              "group=<GROUP>"  parameter specifies  the name  of group
+              this backend address belongs to.  By default, it belongs
+              to  the unnamed  default group.   The name  of group  is
+              unique   per   pattern.   "group-weight=<N>"   parameter
+              specifies the  weight of  the group.  The  higher weight
+              gets  more frequently  selected  by  the load  balancing
+              algorithm.  <N> must be  [1, 256] inclusive.  The weight
+              8 has 4 times more weight  than 2.  <N> must be the same
+              for  all addresses  which  share the  same <GROUP>.   If
+              "group-weight" is  omitted in an address,  but the other
+              address  which  belongs  to  the  same  group  specifies
+              "group-weight",   its    weight   is   used.     If   no
+              "group-weight"  is  specified  for  all  addresses,  the
+              weight of a group becomes 1.  "group" and "group-weight"
+              are ignored if session affinity is enabled.
+
+              "weight=<N>"  parameter  specifies  the  weight  of  the
+              backend  address  inside  a  group  which  this  address
+              belongs  to.  The  higher  weight  gets more  frequently
+              selected by  the load balancing algorithm.   <N> must be
+              [1,  256] inclusive.   The  weight 8  has  4 times  more
+              weight  than weight  2.  If  this parameter  is omitted,
+              weight  becomes  1.   "weight"  is  ignored  if  session
+              affinity is enabled.
+
               Since ";" and ":" are  used as delimiter, <PATTERN> must
               not  contain these  characters.  Since  ";" has  special
               meaning in shell, the option value must be quoted.
@@ -2871,6 +2898,8 @@ int process_options(Config *config,
     assert(include_set.empty());
   }
 
+  Log::set_severity_level(config->logging.severity);
+
   auto &loggingconf = config->logging;
 
   if (loggingconf.access.syslog || loggingconf.error.syslog) {
@@ -3084,7 +3113,7 @@ int process_options(Config *config,
 
   auto &fwdconf = config->http.forwarded;
 
-  if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED &&
+  if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED &&
       fwdconf.by_obfuscated.empty()) {
     // 2 for '_' and terminal NULL
     auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2);
@@ -3140,7 +3169,7 @@ void reload_config(WorkerProcess *wp) {
   LOG(NOTICE) << "Reloading configuration";
 
   auto cur_config = mod_config();
-  auto new_config = make_unique<Config>();
+  auto new_config = std::make_unique<Config>();
 
   fill_default_config(new_config.get());
 
@@ -3195,7 +3224,7 @@ void reload_config(WorkerProcess *wp) {
   // We no longer use signals for this worker.
   last_wp->shutdown_signal_watchers();
 
-  worker_process_add(make_unique<WorkerProcess>(loop, pid, ipc_fd));
+  worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd));
 
   if (!get_config()->pid_file.empty()) {
     save_pid();
index c45f92b..063eed7 100644 (file)
@@ -41,7 +41,7 @@ namespace shrpx {
 namespace {
 // List of API endpoints
 const std::array<APIEndpoint, 2> &apis() {
-  static const auto apis = new std::array<APIEndpoint, 2>{{
+  static const auto apis = new std::array<APIEndpoint, 2>{
       APIEndpoint{
           StringRef::from_lit("/api/v1beta1/backendconfig"),
           true,
@@ -54,7 +54,7 @@ const std::array<APIEndpoint, 2> &apis() {
           (1 << API_METHOD_GET),
           &APIDownstreamConnection::handle_configrevision,
       },
-  }};
+  };
 
   return *apis;
 }
@@ -95,15 +95,9 @@ void APIDownstreamConnection::detach_downstream(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) {
+                                        APIStatusCode api_status,
+                                        const StringRef &data) {
   shutdown_read_ = true;
 
   auto upstream = downstream_->get_upstream();
@@ -117,10 +111,10 @@ int APIDownstreamConnection::send_reply(unsigned int http_status,
   StringRef api_status_str;
 
   switch (api_status) {
-  case API_SUCCESS:
+  case APIStatusCode::SUCCESS:
     api_status_str = StringRef::from_lit("Success");
     break;
-  case API_FAILURE:
+  case APIStatusCode::FAILURE:
     api_status_str = StringRef::from_lit("Failure");
     break;
   default:
@@ -206,7 +200,7 @@ int APIDownstreamConnection::push_request_headers() {
   api_ = lookup_api(path);
 
   if (!api_) {
-    send_reply(404, API_FAILURE);
+    send_reply(404, APIStatusCode::FAILURE);
 
     return 0;
   }
@@ -238,7 +232,7 @@ int APIDownstreamConnection::push_request_headers() {
   // 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);
+    send_reply(413, APIStatusCode::FAILURE);
 
     return 0;
   }
@@ -253,7 +247,7 @@ int APIDownstreamConnection::push_request_headers() {
     fd_ = mkstemp(tempname);
 #endif // !HAVE_MKOSTEMP
     if (fd_ == -1) {
-      send_reply(500, API_FAILURE);
+      send_reply(500, APIStatusCode::FAILURE);
 
       return 0;
     }
@@ -303,7 +297,7 @@ int APIDownstreamConnection::error_method_not_allowed() {
 
   resp.fs.add_header_token(StringRef::from_lit("allow"), StringRef{iov.base, p},
                            false, -1);
-  return send_reply(405, API_FAILURE);
+  return send_reply(405, APIStatusCode::FAILURE);
 }
 
 int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
@@ -316,7 +310,7 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
   auto &apiconf = get_config()->api;
 
   if (static_cast<size_t>(req.recv_body_length) > apiconf.max_request_body) {
-    send_reply(413, API_FAILURE);
+    send_reply(413, APIStatusCode::FAILURE);
 
     return 0;
   }
@@ -327,7 +321,7 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
   if (nwrite == -1) {
     auto error = errno;
     LOG(ERROR) << "Could not write API request body: errno=" << error;
-    send_reply(500, API_FAILURE);
+    send_reply(500, APIStatusCode::FAILURE);
 
     return 0;
   }
@@ -351,14 +345,15 @@ int APIDownstreamConnection::handle_backendconfig() {
   auto &req = downstream_->request();
 
   if (req.recv_body_length == 0) {
-    send_reply(200, API_SUCCESS);
+    send_reply(200, APIStatusCode::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);
+    send_reply(500, APIStatusCode::FAILURE);
+    return 0;
   }
 
   auto unmapper = defer(munmap, rp, req.recv_body_length);
@@ -395,7 +390,7 @@ int APIDownstreamConnection::handle_backendconfig() {
 
     auto eq = std::find(first, eol, '=');
     if (eq == eol) {
-      send_reply(400, API_FAILURE);
+      send_reply(400, APIStatusCode::FAILURE);
       return 0;
     }
 
@@ -414,7 +409,7 @@ int APIDownstreamConnection::handle_backendconfig() {
 
     if (parse_config(&new_config, optid, opt, optval, include_set,
                      pattern_addr_indexer) != 0) {
-      send_reply(400, API_FAILURE);
+      send_reply(400, APIStatusCode::FAILURE);
       return 0;
     }
 
@@ -424,7 +419,7 @@ int APIDownstreamConnection::handle_backendconfig() {
   auto &tlsconf = config->tls;
   if (configure_downstream_group(&new_config, config->http2_proxy, true,
                                  tlsconf) != 0) {
-    send_reply(400, API_FAILURE);
+    send_reply(400, APIStatusCode::FAILURE);
     return 0;
   }
 
@@ -432,7 +427,7 @@ int APIDownstreamConnection::handle_backendconfig() {
 
   conn_handler->send_replace_downstream(downstreamconf);
 
-  send_reply(200, API_SUCCESS);
+  send_reply(200, APIStatusCode::SUCCESS);
 
   return 0;
 }
@@ -451,7 +446,7 @@ int APIDownstreamConnection::handle_configrevision() {
       util::make_string_ref_uint(balloc, config->config_revision),
       StringRef::from_lit("}"));
 
-  send_reply(200, API_SUCCESS, data);
+  send_reply(200, APIStatusCode::SUCCESS, data);
 
   return 0;
 }
index 4a230dc..af6b95e 100644 (file)
@@ -43,6 +43,13 @@ enum APIMethod {
   API_METHOD_MAX,
 };
 
+// API status code, which is independent from HTTP status code.  But
+// generally, 2xx code for SUCCESS, and otherwise FAILURE.
+enum class APIStatusCode {
+  SUCCESS,
+  FAILURE,
+};
+
 class APIDownstreamConnection;
 
 struct APIEndpoint {
@@ -83,7 +90,7 @@ public:
   get_downstream_addr_group() const;
   virtual DownstreamAddr *get_addr() const;
 
-  int send_reply(unsigned int http_status, int api_status,
+  int send_reply(unsigned int http_status, APIStatusCode api_status,
                  const StringRef &data = StringRef{});
   int error_method_not_allowed();
 
index d718edd..e118008 100644 (file)
@@ -111,6 +111,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
 int ClientHandler::noop() { return 0; }
 
 int ClientHandler::read_clear() {
+  auto should_break = false;
   rb_.ensure_chunk();
   for (;;) {
     if (rb_.rleft() && on_read() != 0) {
@@ -123,7 +124,7 @@ int ClientHandler::read_clear() {
       return 0;
     }
 
-    if (!ev_is_active(&conn_.rev)) {
+    if (!ev_is_active(&conn_.rev) || should_break) {
       return 0;
     }
 
@@ -141,6 +142,7 @@ int ClientHandler::read_clear() {
     }
 
     rb_.write(nread);
+    should_break = true;
   }
 }
 
@@ -205,6 +207,8 @@ int ClientHandler::tls_handshake() {
 }
 
 int ClientHandler::read_tls() {
+  auto should_break = false;
+
   ERR_clear_error();
 
   rb_.ensure_chunk();
@@ -221,7 +225,7 @@ int ClientHandler::read_tls() {
       return 0;
     }
 
-    if (!ev_is_active(&conn_.rev)) {
+    if (!ev_is_active(&conn_.rev) || should_break) {
       return 0;
     }
 
@@ -239,6 +243,7 @@ int ClientHandler::read_tls() {
     }
 
     rb_.write(nread);
+    should_break = true;
   }
 }
 
@@ -305,7 +310,7 @@ int ClientHandler::upstream_write() {
 
 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_,
+  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)) {
@@ -334,7 +339,7 @@ int ClientHandler::upstream_http2_connhd_read() {
 
 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_,
+  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, "
@@ -396,7 +401,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
             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),
+            get_config()->tls.dyn_rec.idle_timeout, Proto::NONE),
       ipaddr_(make_string_ref(balloc_, ipaddr)),
       port_(make_string_ref(balloc_, port)),
       faddr_(faddr),
@@ -430,7 +435,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
   auto &fwdconf = config->http.forwarded;
 
   if (fwdconf.params & FORWARDED_FOR) {
-    if (fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) {
+    if (fwdconf.for_node_type == ForwardedNode::OBFUSCATED) {
       // 1 for '_'
       auto len = SHRPX_OBFUSCATED_NODE_LENGTH + 1;
       // 1 for terminating NUL.
@@ -478,7 +483,7 @@ void ClientHandler::setup_upstream_io_callback() {
     // 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);
+    upstream_ = std::make_unique<HttpsUpstream>(this);
     alpn_ = StringRef::from_lit("http/1.1");
     read_ = &ClientHandler::read_clear;
     write_ = &ClientHandler::write_clear;
@@ -584,7 +589,7 @@ int ClientHandler::validate_next_proto() {
   if (util::check_h2_is_selected(proto)) {
     on_read_ = &ClientHandler::upstream_http2_connhd_read;
 
-    auto http2_upstream = make_unique<Http2Upstream>(this);
+    auto http2_upstream = std::make_unique<Http2Upstream>(this);
 
     upstream_ = std::move(http2_upstream);
     alpn_ = make_string_ref(balloc_, proto);
@@ -600,7 +605,7 @@ int ClientHandler::validate_next_proto() {
   }
 
   if (proto == StringRef::from_lit("http/1.1")) {
-    upstream_ = make_unique<HttpsUpstream>(this);
+    upstream_ = std::make_unique<HttpsUpstream>(this);
     alpn_ = StringRef::from_lit("http/1.1");
 
     // At this point, input buffer is already filled with some bytes.
@@ -658,30 +663,11 @@ void ClientHandler::pool_downstream_connection(
                      << " 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) {
@@ -701,7 +687,7 @@ uint32_t compute_affinity_from_ip(const StringRef &ip) {
 }
 } // namespace
 
-Http2Session *ClientHandler::select_http2_session_with_affinity(
+Http2Session *ClientHandler::get_http2_session(
     const std::shared_ptr<DownstreamAddrGroup> &group, DownstreamAddr *addr) {
   auto &shared_addr = group->shared_addr;
 
@@ -754,171 +740,6 @@ Http2Session *ClientHandler::select_http2_session_with_affinity(
   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);
@@ -937,83 +758,60 @@ uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
   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();
+namespace {
+void reschedule_addr(
+    std::priority_queue<DownstreamAddrEntry, std::vector<DownstreamAddrEntry>,
+                        DownstreamAddrEntryGreater> &pq,
+    DownstreamAddr *addr) {
+  auto penalty = MAX_DOWNSTREAM_ADDR_WEIGHT + addr->pending_penalty;
+  addr->cycle += penalty / addr->weight;
+  addr->pending_penalty = penalty % addr->weight;
+
+  pq.push(DownstreamAddrEntry{addr, addr->seq, addr->cycle});
+  addr->queued = true;
+}
+} // namespace
 
-  const auto &req = downstream->request();
+namespace {
+void reschedule_wg(
+    std::priority_queue<WeightGroupEntry, std::vector<WeightGroupEntry>,
+                        WeightGroupEntryGreater> &pq,
+    WeightGroup *wg) {
+  auto penalty = MAX_DOWNSTREAM_ADDR_WEIGHT + wg->pending_penalty;
+  wg->cycle += penalty / wg->weight;
+  wg->pending_penalty = penalty % wg->weight;
+
+  pq.push(WeightGroupEntry{wg, wg->seq, wg->cycle});
+  wg->queued = true;
+}
+} // namespace
 
+DownstreamAddr *ClientHandler::get_downstream_addr(int &err,
+                                                   DownstreamAddrGroup *group,
+                                                   Downstream *downstream) {
   err = 0;
 
   switch (faddr_->alt_mode) {
-  case ALTMODE_API:
-    return make_unique<APIDownstreamConnection>(worker_);
-  case ALTMODE_HEALTHMON:
-    return make_unique<HealthMonitorDownstreamConnection>();
+  case UpstreamAltMode::API:
+  case UpstreamAltMode::HEALTHMON:
+    assert(0);
+  default:
+    break;
   }
 
-  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.regular_connect_method()) {
-      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) {
+  if (shared_addr->affinity.type != SessionAffinity::NONE) {
     uint32_t hash;
     switch (shared_addr->affinity.type) {
-    case AFFINITY_IP:
+    case SessionAffinity::IP:
       if (!affinity_hash_computed_) {
         affinity_hash_ = compute_affinity_from_ip(ipaddr_);
         affinity_hash_computed_ = true;
       }
       hash = affinity_hash_;
       break;
-    case AFFINITY_COOKIE:
+    case SessionAffinity::COOKIE:
       hash = get_affinity_cookie(downstream, shared_addr->affinity.cookie.name);
       break;
     default:
@@ -1042,8 +840,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
           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)) {
+        if (addr->connect_blocker->blocked()) {
           continue;
         }
         break;
@@ -1055,109 +852,153 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
       aff_idx = i;
     }
 
-    if (addr->proto == PROTO_HTTP2) {
-      auto http2session = select_http2_session_with_affinity(group, addr);
-
-      auto dconn = make_unique<Http2DownstreamConnection>(http2session);
+    return addr;
+  }
 
-      dconn->set_client_handler(this);
+  auto &wgpq = shared_addr->pq;
 
-      return std::move(dconn);
+  for (;;) {
+    if (wgpq.empty()) {
+      CLOG(INFO, this) << "No working downstream address found";
+      err = -1;
+      return nullptr;
     }
 
-    auto &dconn_pool = addr->dconn_pool;
-    auto dconn = dconn_pool->pop_downstream_connection();
+    auto wg = wgpq.top().wg;
+    wgpq.pop();
+    wg->queued = false;
+
+    for (;;) {
+      if (wg->pq.empty()) {
+        break;
+      }
 
-    if (!dconn) {
-      dconn = make_unique<HttpDownstreamConnection>(group, aff_idx, conn_.loop,
-                                                    worker_);
+      auto addr = wg->pq.top().addr;
+      wg->pq.pop();
+      addr->queued = false;
+
+      if (addr->connect_blocker->blocked()) {
+        continue;
+      }
+
+      reschedule_addr(wg->pq, addr);
+      reschedule_wg(wgpq, wg);
+
+      return addr;
     }
+  }
+}
 
-    dconn->set_client_handler(this);
+std::unique_ptr<DownstreamConnection>
+ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
+  size_t group_idx;
+  auto &downstreamconf = *worker_->get_downstream_config();
+  auto &routerconf = downstreamconf.router;
 
-    return dconn;
+  auto catch_all = downstreamconf.addr_group_catch_all;
+  auto &groups = worker_->get_downstream_addr_groups();
+
+  auto &req = downstream->request();
+
+  err = 0;
+
+  switch (faddr_->alt_mode) {
+  case UpstreamAltMode::API:
+    return std::make_unique<APIDownstreamConnection>(worker_);
+  case UpstreamAltMode::HEALTHMON:
+    return std::make_unique<HealthMonitorDownstreamConnection>();
+  default:
+    break;
   }
 
-  auto http1_weight = shared_addr->http1_pri.weight;
-  auto http2_weight = shared_addr->http2_pri.weight;
+  auto &balloc = downstream->get_block_allocator();
 
-  auto proto = PROTO_NONE;
+  StringRef authority, path;
 
-  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;
+  if (req.forwarded_once) {
+    if (groups.size() != 1) {
+      authority = req.orig_authority;
+      path = req.orig_path;
     }
-  } 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 {
+    if (faddr_->sni_fwd) {
+      authority = sni_;
+    } else if (!req.authority.empty()) {
+      authority = req.authority;
     } else {
-      proto = PROTO_HTTP2;
-      shared_addr->http2_pri.cycle = next_cycle(shared_addr->http2_pri);
+      auto h = req.fs.header(http2::HD_HOST);
+      if (h) {
+        authority = h->value;
+      }
     }
-  } 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";
+    // CONNECT method does not have path.  But we requires path in
+    // host-path mapping.  As workaround, we assume that path is
+    // "/".
+    if (!req.regular_connect_method()) {
+      path = req.path;
     }
 
-    err = -1;
-    return nullptr;
+    // Cache the authority and path used for the first-time backend
+    // selection because per-pattern mruby script can change them.
+    req.orig_authority = authority;
+    req.orig_path = path;
+    req.forwarded_once = true;
   }
 
-  if (proto == PROTO_HTTP2) {
-    if (LOG_ENABLED(INFO)) {
-      CLOG(INFO, this) << "Downstream connection pool is empty."
-                       << " Create new one";
-    }
+  // Fast path.  If we have one group, it must be catch-all group.
+  if (groups.size() == 1) {
+    group_idx = 0;
+  } else {
+    group_idx = match_downstream_addr_group(routerconf, authority, path, groups,
+                                            catch_all, balloc);
+  }
 
-    auto http2session = select_http2_session(group);
+  if (LOG_ENABLED(INFO)) {
+    CLOG(INFO, this) << "Downstream address group_idx: " << group_idx;
+  }
 
-    if (http2session == nullptr) {
-      err = -1;
-      return nullptr;
+  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.";
     }
-
-    auto dconn = make_unique<Http2DownstreamConnection>(http2session);
-
-    dconn->set_client_handler(this);
-
-    return std::move(dconn);
+    err = SHRPX_ERR_TLS_REQUIRED;
+    return nullptr;
   }
 
-  auto &dconn_pool = shared_addr->dconn_pool;
-
-  // pool connection must be HTTP/1.1 connection
-  auto dconn = dconn_pool.pop_downstream_connection();
+  auto &group = groups[group_idx];
+  auto addr = get_downstream_addr(err, group.get(), downstream);
+  if (addr == nullptr) {
+    return nullptr;
+  }
 
-  if (dconn) {
-    if (LOG_ENABLED(INFO)) {
-      CLOG(INFO, this) << "Reuse downstream connection DCONN:" << dconn.get()
-                       << " from pool";
+  if (addr->proto == Proto::HTTP1) {
+    auto dconn = addr->dconn_pool->pop_downstream_connection();
+    if (dconn) {
+      dconn->set_client_handler(this);
+      return dconn;
     }
-  } 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 = std::make_unique<HttpDownstreamConnection>(group, addr, conn_.loop,
+                                                       worker_);
+    dconn->set_client_handler(this);
+    return dconn;
   }
 
-  dconn->set_client_handler(this);
+  if (LOG_ENABLED(INFO)) {
+    CLOG(INFO, this) << "Downstream connection pool is empty."
+                     << " Create new one";
+  }
 
+  auto http2session = get_http2_session(group, addr);
+  auto dconn = std::make_unique<Http2DownstreamConnection>(http2session);
+  dconn->set_client_handler(this);
   return dconn;
 }
 
@@ -1166,14 +1007,14 @@ 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);
+  upstream_ = std::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 upstream = std::make_unique<Http2Upstream>(this);
 
   auto output = upstream->get_response_buf();
 
@@ -1319,7 +1160,7 @@ int ClientHandler::proxy_protocol_read() {
   // 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'}};
+  auto chrs = std::array<char, 2>{'\n', '\0'};
 
   constexpr size_t MAX_PROXY_LINELEN = 107;
 
@@ -1490,7 +1331,7 @@ int ClientHandler::proxy_protocol_read() {
   auto &fwdconf = config->http.forwarded;
 
   if ((fwdconf.params & FORWARDED_FOR) &&
-      fwdconf.for_node_type == FORWARDED_NODE_IP) {
+      fwdconf.for_node_type == ForwardedNode::IP) {
     init_forwarded_for(family, ipaddr_);
   }
 
@@ -1500,7 +1341,7 @@ int ClientHandler::proxy_protocol_read() {
 StringRef ClientHandler::get_forwarded_by() const {
   auto &fwdconf = get_config()->http.forwarded;
 
-  if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) {
+  if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED) {
     return fwdconf.by_obfuscated;
   }
 
index 9e1153b..c31b1ee 100644 (file)
@@ -99,14 +99,14 @@ public:
 
   void pool_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
   void remove_downstream_connection(DownstreamConnection *dconn);
+  DownstreamAddr *get_downstream_addr(int &err, DownstreamAddrGroup *group,
+                                      Downstream *downstream);
   // 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|.
+  // error code to |err|.
   std::unique_ptr<DownstreamConnection>
-  get_downstream_connection(int &err, Downstream *downstream,
-                            shrpx_proto pref_proto = PROTO_NONE);
+  get_downstream_connection(int &err, Downstream *downstream);
   MemchunkPool *get_mcpool();
   SSL *get_ssl() const;
   // Call this function when HTTP/2 connection header is received at
@@ -150,10 +150,8 @@ public:
   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);
+  get_http2_session(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.
index b79a799..769ad9b 100644 (file)
 #include <cerrno>
 #include <limits>
 #include <fstream>
+#include <unordered_map>
 
 #include <nghttp2/nghttp2.h>
 
-#include "http-parser/http_parser.h"
+#include "url-parser/url_parser.h"
 
 #include "shrpx_log.h"
 #include "shrpx_tls.h"
@@ -160,7 +161,7 @@ bool is_secure(const StringRef &filename) {
 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 ticket_keys = std::make_unique<TicketKeys>();
   auto &keys = ticket_keys->keys;
   keys.resize(files.size());
   auto enc_keylen = EVP_CIPHER_key_length(cipher);
@@ -379,7 +380,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[2]) {
     case 'd':
       if (util::strieq_l("pi", name, 2)) {
-        return SHRPX_LOGF_PID;
+        return LogFragmentType::PID;
       }
       break;
     }
@@ -388,7 +389,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[3]) {
     case 'n':
       if (util::strieq_l("alp", name, 3)) {
-        return SHRPX_LOGF_ALPN;
+        return LogFragmentType::ALPN;
       }
       break;
     }
@@ -397,7 +398,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[5]) {
     case 's':
       if (util::strieq_l("statu", name, 5)) {
-        return SHRPX_LOGF_STATUS;
+        return LogFragmentType::STATUS;
       }
       break;
     }
@@ -406,12 +407,12 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[6]) {
     case 'i':
       if (util::strieq_l("tls_sn", name, 6)) {
-        return SHRPX_LOGF_TLS_SNI;
+        return LogFragmentType::TLS_SNI;
       }
       break;
     case 't':
       if (util::strieq_l("reques", name, 6)) {
-        return SHRPX_LOGF_REQUEST;
+        return LogFragmentType::REQUEST;
       }
       break;
     }
@@ -420,15 +421,15 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[9]) {
     case 'l':
       if (util::strieq_l("time_loca", name, 9)) {
-        return SHRPX_LOGF_TIME_LOCAL;
+        return LogFragmentType::TIME_LOCAL;
       }
       break;
     case 'r':
       if (util::strieq_l("ssl_ciphe", name, 9)) {
-        return SHRPX_LOGF_SSL_CIPHER;
+        return LogFragmentType::SSL_CIPHER;
       }
       if (util::strieq_l("tls_ciphe", name, 9)) {
-        return SHRPX_LOGF_TLS_CIPHER;
+        return LogFragmentType::TLS_CIPHER;
       }
       break;
     }
@@ -437,15 +438,15 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[10]) {
     case 'r':
       if (util::strieq_l("remote_add", name, 10)) {
-        return SHRPX_LOGF_REMOTE_ADDR;
+        return LogFragmentType::REMOTE_ADDR;
       }
       break;
     case 't':
       if (util::strieq_l("remote_por", name, 10)) {
-        return SHRPX_LOGF_REMOTE_PORT;
+        return LogFragmentType::REMOTE_PORT;
       }
       if (util::strieq_l("server_por", name, 10)) {
-        return SHRPX_LOGF_SERVER_PORT;
+        return LogFragmentType::SERVER_PORT;
       }
       break;
     }
@@ -454,28 +455,28 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[11]) {
     case '1':
       if (util::strieq_l("time_iso860", name, 11)) {
-        return SHRPX_LOGF_TIME_ISO8601;
+        return LogFragmentType::TIME_ISO8601;
       }
       break;
     case 'e':
       if (util::strieq_l("request_tim", name, 11)) {
-        return SHRPX_LOGF_REQUEST_TIME;
+        return LogFragmentType::REQUEST_TIME;
       }
       break;
     case 'l':
       if (util::strieq_l("ssl_protoco", name, 11)) {
-        return SHRPX_LOGF_SSL_PROTOCOL;
+        return LogFragmentType::SSL_PROTOCOL;
       }
       if (util::strieq_l("tls_protoco", name, 11)) {
-        return SHRPX_LOGF_TLS_PROTOCOL;
+        return LogFragmentType::TLS_PROTOCOL;
       }
       break;
     case 't':
       if (util::strieq_l("backend_hos", name, 11)) {
-        return SHRPX_LOGF_BACKEND_HOST;
+        return LogFragmentType::BACKEND_HOST;
       }
       if (util::strieq_l("backend_por", name, 11)) {
-        return SHRPX_LOGF_BACKEND_PORT;
+        return LogFragmentType::BACKEND_PORT;
       }
       break;
     }
@@ -484,10 +485,10 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[13]) {
     case 'd':
       if (util::strieq_l("ssl_session_i", name, 13)) {
-        return SHRPX_LOGF_SSL_SESSION_ID;
+        return LogFragmentType::SSL_SESSION_ID;
       }
       if (util::strieq_l("tls_session_i", name, 13)) {
-        return SHRPX_LOGF_TLS_SESSION_ID;
+        return LogFragmentType::TLS_SESSION_ID;
       }
       break;
     }
@@ -496,7 +497,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[14]) {
     case 't':
       if (util::strieq_l("body_bytes_sen", name, 14)) {
-        return SHRPX_LOGF_BODY_BYTES_SENT;
+        return LogFragmentType::BODY_BYTES_SENT;
       }
       break;
     }
@@ -505,7 +506,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[16]) {
     case 'l':
       if (util::strieq_l("tls_client_seria", name, 16)) {
-        return SHRPX_LOGF_TLS_CLIENT_SERIAL;
+        return LogFragmentType::TLS_CLIENT_SERIAL;
       }
       break;
     }
@@ -514,10 +515,10 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[17]) {
     case 'd':
       if (util::strieq_l("ssl_session_reuse", name, 17)) {
-        return SHRPX_LOGF_SSL_SESSION_REUSED;
+        return LogFragmentType::SSL_SESSION_REUSED;
       }
       if (util::strieq_l("tls_session_reuse", name, 17)) {
-        return SHRPX_LOGF_TLS_SESSION_REUSED;
+        return LogFragmentType::TLS_SESSION_REUSED;
       }
       break;
     }
@@ -526,7 +527,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[21]) {
     case 'e':
       if (util::strieq_l("tls_client_issuer_nam", name, 21)) {
-        return SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME;
+        return LogFragmentType::TLS_CLIENT_ISSUER_NAME;
       }
       break;
     }
@@ -535,7 +536,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[22]) {
     case 'e':
       if (util::strieq_l("tls_client_subject_nam", name, 22)) {
-        return SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME;
+        return LogFragmentType::TLS_CLIENT_SUBJECT_NAME;
       }
       break;
     }
@@ -544,7 +545,7 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[26]) {
     case '1':
       if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) {
-        return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1;
+        return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1;
       }
       break;
     }
@@ -553,13 +554,13 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     switch (name[28]) {
     case '6':
       if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) {
-        return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256;
+        return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256;
       }
       break;
     }
     break;
   }
-  return SHRPX_LOGF_NONE;
+  return LogFragmentType::NONE;
 }
 } // namespace
 
@@ -613,16 +614,16 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
 
     auto type = log_var_lookup_token(var_name, var_namelen);
 
-    if (type == SHRPX_LOGF_NONE) {
+    if (type == LogFragmentType::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;
+          type = LogFragmentType::AUTHORITY;
         } else {
-          type = SHRPX_LOGF_HTTP;
+          type = LogFragmentType::HTTP;
           value = var_name + str_size("http_");
         }
       } else {
@@ -634,7 +635,7 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
 
     if (literal_start < var_start) {
       res.emplace_back(
-          SHRPX_LOGF_LITERAL,
+          LogFragmentType::LITERAL,
           make_string_ref(balloc, StringRef{literal_start, var_start}));
     }
 
@@ -661,7 +662,7 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
   }
 
   if (literal_start != eop) {
-    res.emplace_back(SHRPX_LOGF_LITERAL,
+    res.emplace_back(LogFragmentType::LITERAL,
                      make_string_ref(balloc, StringRef{literal_start, eop}));
   }
 
@@ -756,7 +757,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
 } // namespace
 
 struct UpstreamParams {
-  int alt_mode;
+  UpstreamAltMode alt_mode;
   bool tls;
   bool sni_fwd;
   bool proxyproto;
@@ -779,17 +780,19 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
     } 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) {
+      if (out.alt_mode != UpstreamAltMode::NONE &&
+          out.alt_mode != UpstreamAltMode::API) {
         LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
         return -1;
       }
-      out.alt_mode = ALTMODE_API;
+      out.alt_mode = UpstreamAltMode::API;
     } else if (util::strieq_l("healthmon", param)) {
-      if (out.alt_mode && out.alt_mode != ALTMODE_HEALTHMON) {
+      if (out.alt_mode != UpstreamAltMode::NONE &&
+          out.alt_mode != UpstreamAltMode::HEALTHMON) {
         LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
         return -1;
       }
-      out.alt_mode = ALTMODE_HEALTHMON;
+      out.alt_mode = UpstreamAltMode::HEALTHMON;
     } else if (util::strieq_l("proxyproto", param)) {
       out.proxyproto = true;
     } else if (!param.empty()) {
@@ -811,12 +814,15 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
 struct DownstreamParams {
   StringRef sni;
   StringRef mruby;
+  StringRef group;
   AffinityConfig affinity;
   ev_tstamp read_timeout;
   ev_tstamp write_timeout;
   size_t fall;
   size_t rise;
-  shrpx_proto proto;
+  uint32_t weight;
+  uint32_t group_weight;
+  Proto proto;
   bool tls;
   bool dns;
   bool redirect_if_not_tls;
@@ -858,10 +864,10 @@ int parse_downstream_params(DownstreamParams &out,
       }
 
       if (util::streq_l("h2", std::begin(protostr), protostr.size())) {
-        out.proto = PROTO_HTTP2;
+        out.proto = Proto::HTTP2;
       } else if (util::streq_l("http/1.1", std::begin(protostr),
                                protostr.size())) {
-        out.proto = PROTO_HTTP1;
+        out.proto = Proto::HTTP1;
       } else {
         LOG(ERROR) << "backend: proto: unknown protocol " << protostr;
         return -1;
@@ -903,11 +909,11 @@ int parse_downstream_params(DownstreamParams &out,
     } 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;
+        out.affinity.type = SessionAffinity::NONE;
       } else if (util::strieq_l("ip", valstr)) {
-        out.affinity.type = AFFINITY_IP;
+        out.affinity.type = SessionAffinity::IP;
       } else if (util::strieq_l("cookie", valstr)) {
-        out.affinity.type = AFFINITY_COOKIE;
+        out.affinity.type = SessionAffinity::COOKIE;
       } else {
         LOG(ERROR)
             << "backend: affinity: value must be one of none, ip, and cookie";
@@ -927,11 +933,11 @@ int parse_downstream_params(DownstreamParams &out,
     } 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;
+        out.affinity.cookie.secure = SessionAffinityCookieSecure::AUTO;
       } else if (util::strieq_l("yes", valstr)) {
-        out.affinity.cookie.secure = COOKIE_SECURE_YES;
+        out.affinity.cookie.secure = SessionAffinityCookieSecure::YES;
       } else if (util::strieq_l("no", valstr)) {
-        out.affinity.cookie.secure = COOKIE_SECURE_NO;
+        out.affinity.cookie.secure = SessionAffinityCookieSecure::NO;
       } else {
         LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
                       "auto, yes, and no";
@@ -958,6 +964,43 @@ int parse_downstream_params(DownstreamParams &out,
               StringRef{first + str_size("write-timeout="), end}) == -1) {
         return -1;
       }
+    } else if (util::istarts_with_l(param, "weight=")) {
+      auto valstr = StringRef{first + str_size("weight="), end};
+      if (valstr.empty()) {
+        LOG(ERROR)
+            << "backend: weight: non-negative integer [1, 256] is expected";
+        return -1;
+      }
+
+      auto n = util::parse_uint(valstr);
+      if (n < 1 || n > 256) {
+        LOG(ERROR)
+            << "backend: weight: non-negative integer [1, 256] is expected";
+        return -1;
+      }
+      out.weight = n;
+    } else if (util::istarts_with_l(param, "group=")) {
+      auto valstr = StringRef{first + str_size("group="), end};
+      if (valstr.empty()) {
+        LOG(ERROR) << "backend: group: empty string is not allowed";
+        return -1;
+      }
+      out.group = valstr;
+    } else if (util::istarts_with_l(param, "group-weight=")) {
+      auto valstr = StringRef{first + str_size("group-weight="), end};
+      if (valstr.empty()) {
+        LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is "
+                      "expected";
+        return -1;
+      }
+
+      auto n = util::parse_uint(valstr);
+      if (n < 1 || n > 256) {
+        LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is "
+                      "expected";
+        return -1;
+      }
+      out.group_weight = n;
     } else if (!param.empty()) {
       LOG(ERROR) << "backend: " << param << ": unknown keyword";
       return -1;
@@ -993,7 +1036,8 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
   auto &addr_groups = downstreamconf.addr_groups;
 
   DownstreamParams params{};
-  params.proto = PROTO_HTTP1;
+  params.proto = Proto::HTTP1;
+  params.weight = 1;
 
   if (parse_downstream_params(params, src_params) != 0) {
     return -1;
@@ -1004,7 +1048,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
     return -1;
   }
 
-  if (params.affinity.type == AFFINITY_COOKIE &&
+  if (params.affinity.type == SessionAffinity::COOKIE &&
       params.affinity.cookie.name.empty()) {
     LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
                   "affinity=cookie is specified";
@@ -1013,6 +1057,9 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
 
   addr.fall = params.fall;
   addr.rise = params.rise;
+  addr.weight = params.weight;
+  addr.group = make_string_ref(downstreamconf.balloc, params.group);
+  addr.group_weight = params.group_weight;
   addr.proto = params.proto;
   addr.tls = params.tls;
   addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
@@ -1056,10 +1103,10 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
       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) {
+      if (params.affinity.type != SessionAffinity::NONE) {
+        if (g.affinity.type == SessionAffinity::NONE) {
           g.affinity.type = params.affinity.type;
-          if (params.affinity.type == AFFINITY_COOKIE) {
+          if (params.affinity.type == SessionAffinity::COOKIE) {
             g.affinity.cookie.name = make_string_ref(
                 downstreamconf.balloc, params.affinity.cookie.name);
             if (!params.affinity.cookie.path.empty()) {
@@ -1129,7 +1176,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
     auto &g = addr_groups.back();
     g.addrs.push_back(addr);
     g.affinity.type = params.affinity.type;
-    if (params.affinity.type == AFFINITY_COOKIE) {
+    if (params.affinity.type == SessionAffinity::COOKIE) {
       g.affinity.cookie.name =
           make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
       if (!params.affinity.cookie.path.empty()) {
@@ -1195,27 +1242,27 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
 } // namespace
 
 namespace {
-int parse_forwarded_node_type(const StringRef &optarg) {
+ForwardedNode parse_forwarded_node_type(const StringRef &optarg) {
   if (util::strieq_l("obfuscated", optarg)) {
-    return FORWARDED_NODE_OBFUSCATED;
+    return ForwardedNode::OBFUSCATED;
   }
 
   if (util::strieq_l("ip", optarg)) {
-    return FORWARDED_NODE_IP;
+    return ForwardedNode::IP;
   }
 
   if (optarg.size() < 2 || optarg[0] != '_') {
-    return -1;
+    return static_cast<ForwardedNode>(-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 static_cast<ForwardedNode>(-1);
   }
 
-  return FORWARDED_NODE_OBFUSCATED;
+  return ForwardedNode::OBFUSCATED;
 }
 } // namespace
 
@@ -2572,7 +2619,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     addr.alt_mode = params.alt_mode;
     addr.accept_proxy_protocol = params.proxyproto;
 
-    if (addr.alt_mode == ALTMODE_API) {
+    if (addr.alt_mode == UpstreamAltMode::API) {
       apiconf.enabled = true;
     }
 
@@ -2635,13 +2682,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
 
     return 0;
   }
-  case SHRPX_OPTID_LOG_LEVEL:
-    if (Log::set_severity_level_by_name(optarg) == -1) {
+  case SHRPX_OPTID_LOG_LEVEL: {
+    auto level = Log::get_severity_level_by_name(optarg);
+    if (level == -1) {
       LOG(ERROR) << opt << ": Invalid severity level: " << optarg;
       return -1;
     }
+    config->logging.severity = level;
 
     return 0;
+  }
   case SHRPX_OPTID_DAEMON:
     config->daemon = util::strieq_l("yes", optarg);
 
@@ -3387,7 +3437,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
   case SHRPX_OPTID_FORWARDED_FOR: {
     auto type = parse_forwarded_node_type(optarg);
 
-    if (type == -1 ||
+    if (type == static_cast<ForwardedNode>(-1) ||
         (optid == SHRPX_OPTID_FORWARDED_FOR && optarg[0] == '_')) {
       LOG(ERROR) << opt << ": unknown node type or illegal obfuscated string "
                  << optarg;
@@ -3398,7 +3448,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
 
     switch (optid) {
     case SHRPX_OPTID_FORWARDED_BY:
-      fwdconf.by_node_type = static_cast<shrpx_forwarded_node_type>(type);
+      fwdconf.by_node_type = type;
       if (optarg[0] == '_') {
         fwdconf.by_obfuscated = make_string_ref(config->balloc, optarg);
       } else {
@@ -3406,7 +3456,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
       }
       break;
     case SHRPX_OPTID_FORWARDED_FOR:
-      fwdconf.for_node_type = static_cast<shrpx_forwarded_node_type>(type);
+      fwdconf.for_node_type = type;
       break;
     }
 
@@ -3872,15 +3922,15 @@ int int_syslog_facility(const StringRef &strfacility) {
   return -1;
 }
 
-StringRef strproto(shrpx_proto proto) {
+StringRef strproto(Proto proto) {
   switch (proto) {
-  case PROTO_NONE:
+  case Proto::NONE:
     return StringRef::from_lit("none");
-  case PROTO_HTTP1:
+  case Proto::HTTP1:
     return StringRef::from_lit("http/1.1");
-  case PROTO_HTTP2:
+  case Proto::HTTP2:
     return StringRef::from_lit("h2");
-  case PROTO_MEMCACHED:
+  case Proto::MEMCACHED:
     return StringRef::from_lit("memcached");
   }
 
@@ -3943,7 +3993,9 @@ int configure_downstream_group(Config *config, bool http2_proxy,
     DownstreamAddrConfig addr{};
     addr.host = StringRef::from_lit(DEFAULT_DOWNSTREAM_HOST);
     addr.port = DEFAULT_DOWNSTREAM_PORT;
-    addr.proto = PROTO_HTTP1;
+    addr.proto = Proto::HTTP1;
+    addr.weight = 1;
+    addr.group_weight = 1;
 
     DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
     g.addrs.push_back(std::move(addr));
@@ -4023,7 +4075,17 @@ int configure_downstream_group(Config *config, bool http2_proxy,
   auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0;
 
   for (auto &g : addr_groups) {
+    std::unordered_map<StringRef, uint32_t> wgchk;
     for (auto &addr : g.addrs) {
+      if (addr.group_weight) {
+        auto it = wgchk.find(addr.group);
+        if (it == std::end(wgchk)) {
+          wgchk.emplace(addr.group, addr.group_weight);
+        } else if ((*it).second != addr.group_weight) {
+          LOG(FATAL) << "backend: inconsistent group-weight for a single group";
+          return -1;
+        }
+      }
 
       if (addr.host_unix) {
         // for AF_UNIX socket, we use "localhost" as host for backend
@@ -4076,7 +4138,18 @@ int configure_downstream_group(Config *config, bool http2_proxy,
       }
     }
 
-    if (g.affinity.type != AFFINITY_NONE) {
+    for (auto &addr : g.addrs) {
+      if (addr.group_weight == 0) {
+        auto it = wgchk.find(addr.group);
+        if (it == std::end(wgchk)) {
+          addr.group_weight = 1;
+        } else {
+          addr.group_weight = (*it).second;
+        }
+      }
+    }
+
+    if (g.affinity.type != SessionAffinity::NONE) {
       size_t idx = 0;
       for (auto &addr : g.addrs) {
         StringRef key;
index 3b60e77..ccc6b4a 100644 (file)
@@ -362,37 +362,42 @@ 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 class Proto {
+  NONE,
+  HTTP1,
+  HTTP2,
+  MEMCACHED,
+};
 
-enum shrpx_session_affinity {
+enum class SessionAffinity {
   // No session affinity
-  AFFINITY_NONE,
+  NONE,
   // Client IP affinity
-  AFFINITY_IP,
+  IP,
   // Cookie based affinity
-  AFFINITY_COOKIE,
+  COOKIE,
 };
 
-enum shrpx_cookie_secure {
+enum class SessionAffinityCookieSecure {
   // Secure attribute of session affinity cookie is determined by the
   // request scheme.
-  COOKIE_SECURE_AUTO,
+  AUTO,
   // Secure attribute of session affinity cookie is always set.
-  COOKIE_SECURE_YES,
+  YES,
   // Secure attribute of session affinity cookie is always unset.
-  COOKIE_SECURE_NO,
+  NO,
 };
 
 struct AffinityConfig {
   // Type of session affinity.
-  shrpx_session_affinity type;
+  SessionAffinity 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;
+    SessionAffinityCookieSecure secure;
   } cookie;
 };
 
@@ -404,9 +409,9 @@ enum shrpx_forwarded_param {
   FORWARDED_PROTO = 0x8,
 };
 
-enum shrpx_forwarded_node_type {
-  FORWARDED_NODE_OBFUSCATED,
-  FORWARDED_NODE_IP,
+enum class ForwardedNode {
+  OBFUSCATED,
+  IP,
 };
 
 struct AltSvc {
@@ -415,13 +420,13 @@ struct AltSvc {
   uint16_t port;
 };
 
-enum UpstreamAltMode {
+enum class UpstreamAltMode {
   // No alternative mode
-  ALTMODE_NONE,
+  NONE,
   // API processing mode
-  ALTMODE_API,
+  API,
   // Health monitor mode
-  ALTMODE_HEALTHMON,
+  HEALTHMON,
 };
 
 struct UpstreamAddr {
@@ -439,7 +444,7 @@ struct UpstreamAddr {
   // domain socket, this is 0.
   int family;
   // Alternate mode
-  int alt_mode;
+  UpstreamAltMode alt_mode;
   // true if |host| contains UNIX domain socket path.
   bool host_unix;
   // true if TLS is enabled.
@@ -463,10 +468,17 @@ struct DownstreamAddrConfig {
   StringRef hostport;
   // hostname sent as SNI field
   StringRef sni;
+  // name of group which this address belongs to.
+  StringRef group;
   size_t fall;
   size_t rise;
+  // weight of this address inside a weight group.  Its range is [1,
+  // 256], inclusive.
+  uint32_t weight;
+  // weight of the weight group.  Its range is [1, 256], inclusive.
+  uint32_t group_weight;
   // Application protocol used in this group
-  shrpx_proto proto;
+  Proto proto;
   // backend port.  0 if |host_unix| is true.
   uint16_t port;
   // true if |host| contains UNIX domain socket path.
@@ -491,13 +503,16 @@ struct AffinityHash {
 
 struct DownstreamAddrGroupConfig {
   DownstreamAddrGroupConfig(const StringRef &pattern)
-      : pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
+      : pattern(pattern),
+        affinity{SessionAffinity::NONE},
+        redirect_if_not_tls(false),
+        timeout{} {}
 
   StringRef pattern;
   StringRef mruby_file;
   std::vector<DownstreamAddrConfig> addrs;
   // Bunch of session affinity hash.  Only used if affinity ==
-  // AFFINITY_IP.
+  // SessionAffinity::IP.
   std::vector<AffinityHash> affinity_hash;
   // Cookie based session affinity configuration.
   AffinityConfig affinity;
@@ -697,10 +712,10 @@ struct HttpConfig {
     uint32_t params;
     // type of value recorded in "by" parameter of Forwarded header
     // field.
-    shrpx_forwarded_node_type by_node_type;
+    ForwardedNode by_node_type;
     // type of value recorded in "for" parameter of Forwarded header
     // field.
-    shrpx_forwarded_node_type for_node_type;
+    ForwardedNode for_node_type;
     bool strip_incoming;
   } forwarded;
   struct {
@@ -794,6 +809,7 @@ struct LoggingConfig {
     bool syslog;
   } error;
   int syslog_facility;
+  int severity;
 };
 
 struct RateLimitConfig {
@@ -1232,7 +1248,7 @@ 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);
+StringRef strproto(Proto proto);
 
 int configure_downstream_group(Config *config, bool http2_proxy,
                                bool numeric_addr_only,
index b970ccb..a8f0962 100644 (file)
@@ -79,84 +79,84 @@ void test_shrpx_config_parse_log_format(void) {
                   R"("${http_referer}" $http_host "$http_user_agent")"));
   CU_ASSERT(16 == res.size());
 
-  CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type);
+  CU_ASSERT(LogFragmentType::REMOTE_ADDR == res[0].type);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[1].type);
+  CU_ASSERT(LogFragmentType::LITERAL == res[1].type);
   CU_ASSERT(" - $remote_user [" == res[1].value);
 
-  CU_ASSERT(SHRPX_LOGF_TIME_LOCAL == res[2].type);
+  CU_ASSERT(LogFragmentType::TIME_LOCAL == res[2].type);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[3].type);
+  CU_ASSERT(LogFragmentType::LITERAL == res[3].type);
   CU_ASSERT("] \"" == res[3].value);
 
-  CU_ASSERT(SHRPX_LOGF_REQUEST == res[4].type);
+  CU_ASSERT(LogFragmentType::REQUEST == res[4].type);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[5].type);
+  CU_ASSERT(LogFragmentType::LITERAL == res[5].type);
   CU_ASSERT("\" " == res[5].value);
 
-  CU_ASSERT(SHRPX_LOGF_STATUS == res[6].type);
+  CU_ASSERT(LogFragmentType::STATUS == res[6].type);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[7].type);
+  CU_ASSERT(LogFragmentType::LITERAL == res[7].type);
   CU_ASSERT(" " == res[7].value);
 
-  CU_ASSERT(SHRPX_LOGF_BODY_BYTES_SENT == res[8].type);
+  CU_ASSERT(LogFragmentType::BODY_BYTES_SENT == res[8].type);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[9].type);
+  CU_ASSERT(LogFragmentType::LITERAL == res[9].type);
   CU_ASSERT(" \"" == res[9].value);
 
-  CU_ASSERT(SHRPX_LOGF_HTTP == res[10].type);
+  CU_ASSERT(LogFragmentType::HTTP == res[10].type);
   CU_ASSERT("referer" == res[10].value);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[11].type);
+  CU_ASSERT(LogFragmentType::LITERAL == res[11].type);
   CU_ASSERT("\" " == res[11].value);
 
-  CU_ASSERT(SHRPX_LOGF_AUTHORITY == res[12].type);
+  CU_ASSERT(LogFragmentType::AUTHORITY == res[12].type);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type);
+  CU_ASSERT(LogFragmentType::LITERAL == res[13].type);
   CU_ASSERT(" \"" == res[13].value);
 
-  CU_ASSERT(SHRPX_LOGF_HTTP == res[14].type);
+  CU_ASSERT(LogFragmentType::HTTP == res[14].type);
   CU_ASSERT("user-agent" == res[14].value);
 
-  CU_ASSERT(SHRPX_LOGF_LITERAL == res[15].type);
+  CU_ASSERT(LogFragmentType::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(LogFragmentType::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(LogFragmentType::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(LogFragmentType::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(LogFragmentType::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(LogFragmentType::LITERAL == res[0].type);
   CU_ASSERT("$" == res[0].value);
 
-  CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[1].type);
+  CU_ASSERT(LogFragmentType::REMOTE_ADDR == res[1].type);
   CU_ASSERT("" == res[1].value);
 }
 
index e261b81..e272d07 100644 (file)
@@ -128,8 +128,16 @@ void ConnectBlocker::online() {
 
 bool ConnectBlocker::in_offline() const { return offline_; }
 
-void ConnectBlocker::call_block_func() { block_func_(); }
+void ConnectBlocker::call_block_func() {
+  if (block_func_) {
+    block_func_();
+  }
+}
 
-void ConnectBlocker::call_unblock_func() { unblock_func_(); }
+void ConnectBlocker::call_unblock_func() {
+  if (unblock_func_) {
+    unblock_func_();
+  }
+}
 
 } // namespace shrpx
index 335e4e4..1937f45 100644 (file)
@@ -59,7 +59,7 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
                        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)
+                       ev_tstamp tls_dyn_rec_idle_timeout, Proto proto)
     : tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool),
           DefaultMemchunks(mcpool)},
       wlimit(loop, &wev, write_limit.rate, write_limit.burst),
@@ -121,7 +121,7 @@ void Connection::disconnect() {
     tls.warmup_writelen = 0;
     tls.last_writelen = 0;
     tls.last_readlen = 0;
-    tls.handshake_state = TLS_CONN_NORMAL;
+    tls.handshake_state = TLSHandshakeState::NORMAL;
     tls.initial_handshake_done = false;
     tls.reneg_started = false;
     tls.sct_requested = false;
@@ -354,9 +354,9 @@ int Connection::tls_handshake() {
   }
 
   switch (tls.handshake_state) {
-  case TLS_CONN_WAIT_FOR_SESSION_CACHE:
+  case TLSHandshakeState::WAIT_FOR_SESSION_CACHE:
     return SHRPX_ERR_INPROGRESS;
-  case TLS_CONN_GOT_SESSION_CACHE: {
+  case TLSHandshakeState::GOT_SESSION_CACHE: {
     // Use the same trick invented by @kazuho in h2o project.
 
     // Discard all outgoing data.
@@ -380,11 +380,13 @@ int Connection::tls_handshake() {
 
     SSL_set_accept_state(tls.ssl);
 
-    tls.handshake_state = TLS_CONN_NORMAL;
+    tls.handshake_state = TLSHandshakeState::NORMAL;
     break;
   }
-  case TLS_CONN_CANCEL_SESSION_CACHE:
-    tls.handshake_state = TLS_CONN_NORMAL;
+  case TLSHandshakeState::CANCEL_SESSION_CACHE:
+    tls.handshake_state = TLSHandshakeState::NORMAL;
+    break;
+  default:
     break;
   }
 
@@ -409,7 +411,7 @@ int Connection::tls_handshake() {
         // 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.handshake_state == TLSHandshakeState::WRITE_STARTED ||
              tls.wbuf.rleft()) &&
             tls.earlybuf.rleft()) {
           rv = 1;
@@ -432,7 +434,7 @@ int Connection::tls_handshake() {
         tls.early_data_finish = true;
         // The same reason stated above.
         if (tlsconf.no_postpone_early_data &&
-            (tls.handshake_state == TLS_CONN_WRITE_STARTED ||
+            (tls.handshake_state == TLSHandshakeState::WRITE_STARTED ||
              tls.wbuf.rleft()) &&
             tls.earlybuf.rleft()) {
           rv = 1;
@@ -467,9 +469,9 @@ int Connection::tls_handshake() {
                   << ERR_error_string(ERR_get_error(), nullptr);
       }
 
-      struct iovec iov;
-      auto iovcnt = tls.wbuf.riovec(&iov, 1);
-      auto nwrite = writev_clear(&iov, iovcnt);
+      struct iovec iov[1];
+      auto iovcnt = tls.wbuf.riovec(iov, 1);
+      auto nwrite = writev_clear(iov, iovcnt);
       if (nwrite > 0) {
         tls.wbuf.drain(nwrite);
       }
@@ -484,7 +486,7 @@ int Connection::tls_handshake() {
     }
   }
 
-  if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE) {
+  if (tls.handshake_state == TLSHandshakeState::WAIT_FOR_SESSION_CACHE) {
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "tls: handshake is still in progress";
     }
@@ -496,8 +498,8 @@ int Connection::tls_handshake() {
   // 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 (tls.handshake_state != TLSHandshakeState::WRITE_STARTED) {
+      tls.handshake_state = TLSHandshakeState::WRITE_STARTED;
       // If peek has already disabled, this is noop.
       tls.rbuf.disable_peek(true);
     }
index d9ca5c0..5d0d79a 100644 (file)
@@ -45,12 +45,12 @@ 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,
+enum class TLSHandshakeState {
+  NORMAL,
+  WAIT_FOR_SESSION_CACHE,
+  GOT_SESSION_CACHE,
+  CANCEL_SESSION_CACHE,
+  WRITE_STARTED,
 };
 
 struct TLSConnection {
@@ -68,7 +68,7 @@ struct TLSConnection {
   // required since these functions require the exact same parameters
   // on non-blocking I/O.
   size_t last_writelen, last_readlen;
-  int handshake_state;
+  TLSHandshakeState handshake_state;
   bool initial_handshake_done;
   bool reneg_started;
   // true if ssl is prepared to do handshake as server.
@@ -100,7 +100,7 @@ struct Connection {
              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);
+             ev_tstamp tls_dyn_rec_idle_timeout, Proto proto);
   ~Connection();
 
   void disconnect();
@@ -169,7 +169,7 @@ struct Connection {
   // 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;
+  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;
index 05bbc3a..84221ba 100644 (file)
@@ -159,9 +159,7 @@ ConnectionHandler::~ConnectionHandler() {
   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;
-    }
+    delete tls_ctx_data;
     SSL_CTX_free(ssl_ctx);
   }
 
@@ -183,7 +181,7 @@ void ConnectionHandler::set_ticket_keys_to_worker(
 void ConnectionHandler::worker_reopen_log_files() {
   WorkerEvent wev{};
 
-  wev.type = REOPEN_LOG;
+  wev.type = WorkerEventType::REOPEN_LOG;
 
   for (auto &worker : workers_) {
     worker->send(wev);
@@ -194,7 +192,7 @@ void ConnectionHandler::worker_replace_downstream(
     std::shared_ptr<DownstreamConfig> downstreamconf) {
   WorkerEvent wev{};
 
-  wev.type = REPLACE_DOWNSTREAM;
+  wev.type = WorkerEventType::REPLACE_DOWNSTREAM;
   wev.downstreamconf = std::move(downstreamconf);
 
   for (auto &worker : workers_) {
@@ -238,7 +236,7 @@ int ConnectionHandler::create_single_worker() {
     }
   }
 
-  single_worker_ = make_unique<Worker>(
+  single_worker_ = std::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
@@ -299,7 +297,7 @@ int ConnectionHandler::create_worker_thread(size_t num) {
   for (size_t i = 0; i < num; ++i) {
     auto loop = ev_loop_new(config->ev_loop_flags);
 
-    auto worker = make_unique<Worker>(
+    auto worker = std::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
@@ -348,7 +346,7 @@ void ConnectionHandler::graceful_shutdown_worker() {
   }
 
   WorkerEvent wev{};
-  wev.type = GRACEFUL_SHUTDOWN;
+  wev.type = WorkerEventType::GRACEFUL_SHUTDOWN;
 
   if (LOG_ENABLED(INFO)) {
     LLOG(INFO, this) << "Sending graceful shutdown signal to worker";
@@ -407,7 +405,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
 
   Worker *worker;
 
-  if (faddr->alt_mode == ALTMODE_API) {
+  if (faddr->alt_mode == UpstreamAltMode::API) {
     worker = workers_[0].get();
 
     if (LOG_ENABLED(INFO)) {
@@ -432,7 +430,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
   }
 
   WorkerEvent wev{};
-  wev.type = NEW_CONNECTION;
+  wev.type = WorkerEventType::NEW_CONNECTION;
   wev.client_fd = fd;
   memcpy(&wev.client_addr, addr, addrlen);
   wev.client_addrlen = addrlen;
@@ -828,7 +826,7 @@ void ConnectionHandler::handle_serial_event() {
 
   for (auto &sev : q) {
     switch (sev.type) {
-    case SEV_REPLACE_DOWNSTREAM:
+    case SerialEventType::REPLACE_DOWNSTREAM:
       // Mmake sure that none of worker uses
       // get_config()->conn.downstream
       mod_config()->conn.downstream = sev.downstreamconf;
@@ -842,13 +840,16 @@ void ConnectionHandler::handle_serial_event() {
       worker_replace_downstream(sev.downstreamconf);
 
       break;
+    default:
+      break;
     }
   }
 }
 
 void ConnectionHandler::send_replace_downstream(
     const std::shared_ptr<DownstreamConfig> &downstreamconf) {
-  send_serial_event(SerialEvent(SEV_REPLACE_DOWNSTREAM, downstreamconf));
+  send_serial_event(
+      SerialEvent(SerialEventType::REPLACE_DOWNSTREAM, downstreamconf));
 }
 
 void ConnectionHandler::send_serial_event(SerialEvent ev) {
index 8a00244..535bc2c 100644 (file)
@@ -84,17 +84,18 @@ struct OCSPUpdateContext {
 };
 
 // SerialEvent is an event sent from Worker thread.
-enum SerialEventType {
-  SEV_NONE,
-  SEV_REPLACE_DOWNSTREAM,
+enum class SerialEventType {
+  NONE,
+  REPLACE_DOWNSTREAM,
 };
 
 struct SerialEvent {
   // ctor for event uses DownstreamConfig
-  SerialEvent(int type, const std::shared_ptr<DownstreamConfig> &downstreamconf)
+  SerialEvent(SerialEventType type,
+              const std::shared_ptr<DownstreamConfig> &downstreamconf)
       : type(type), downstreamconf(downstreamconf) {}
 
-  int type;
+  SerialEventType type;
   std::shared_ptr<DownstreamConfig> downstreamconf;
 };
 
@@ -163,7 +164,8 @@ public:
   void set_neverbleed(neverbleed_t *nb);
 #endif // HAVE_NEVERBLEED
 
-  // Send SerialEvent SEV_REPLACE_DOWNSTREAM to this object.
+  // Send SerialEvent SerialEventType::REPLACE_DOWNSTREAM to this
+  // object.
   void send_replace_downstream(
       const std::shared_ptr<DownstreamConfig> &downstreamconf);
   // Internal function to send |ev| to this object.
index cb77e60..f83ecb7 100644 (file)
@@ -37,7 +37,7 @@ 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) {
+  if (resolv->get_status(nullptr) != DNSResolverStatus::RUNNING) {
     return;
   }
 
@@ -70,10 +70,12 @@ void process_result(DNSResolver *resolv) {
   Address result;
   auto status = resolv->get_status(&result);
   switch (status) {
-  case DNS_STATUS_OK:
-  case DNS_STATUS_ERROR:
+  case DNSResolverStatus::OK:
+  case DNSResolverStatus::ERROR:
     cb(status, &result);
     break;
+  default:
+    break;
   }
   // resolv may be deleted here.
 }
@@ -117,7 +119,7 @@ DNSResolver::DNSResolver(struct ev_loop *loop)
       loop_(loop),
       channel_(nullptr),
       family_(AF_UNSPEC),
-      status_(DNS_STATUS_IDLE) {
+      status_(DNSResolverStatus::IDLE) {
   ev_timer_init(&timer_, timeoutcb, 0., 0.);
   timer_.data = this;
 }
@@ -134,7 +136,7 @@ DNSResolver::~DNSResolver() {
 }
 
 int DNSResolver::resolve(const StringRef &name, int family) {
-  if (status_ != DNS_STATUS_IDLE) {
+  if (status_ != DNSResolverStatus::IDLE) {
     return -1;
   }
 
@@ -164,12 +166,12 @@ int DNSResolver::resolve(const StringRef &name, int family) {
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
     }
-    status_ = DNS_STATUS_ERROR;
+    status_ = DNSResolverStatus::ERROR;
     return -1;
   }
 
   channel_ = chan;
-  status_ = DNS_STATUS_RUNNING;
+  status_ = DNSResolverStatus::RUNNING;
 
   ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
   reset_timeout();
@@ -186,20 +188,19 @@ int DNSResolver::on_timeout() {
 }
 
 int DNSResolver::handle_event(int rfd, int wfd) {
-  if (status_ == DNS_STATUS_IDLE) {
+  if (status_ == DNSResolverStatus::IDLE) {
     return -1;
   }
 
   ares_process_fd(channel_, rfd, wfd);
 
   switch (status_) {
-  case DNS_STATUS_RUNNING: {
+  case DNSResolverStatus::RUNNING:
     reset_timeout();
     return 0;
-  }
-  case DNS_STATUS_OK:
+  case DNSResolverStatus::OK:
     return 0;
-  case DNS_STATUS_ERROR:
+  case DNSResolverStatus::ERROR:
     return -1;
   default:
     // Unreachable
@@ -209,7 +210,7 @@ int DNSResolver::handle_event(int rfd, int wfd) {
 }
 
 void DNSResolver::reset_timeout() {
-  if (status_ != DNS_STATUS_RUNNING) {
+  if (status_ != DNSResolverStatus::RUNNING) {
     return;
   }
   timeval tvout;
@@ -223,8 +224,8 @@ void DNSResolver::reset_timeout() {
   ev_timer_again(loop_, &timer_);
 }
 
-int DNSResolver::get_status(Address *result) const {
-  if (status_ != DNS_STATUS_OK) {
+DNSResolverStatus DNSResolver::get_status(Address *result) const {
+  if (status_ != DNSResolverStatus::OK) {
     return status_;
   }
 
@@ -251,7 +252,7 @@ void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
     }
   }
 
-  auto w = make_unique<ev_io>();
+  auto w = std::make_unique<ev_io>();
   ev_io_init(w.get(), cb, fd, event);
   w->data = data;
   ev_io_start(loop, w.get());
@@ -294,7 +295,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
       LOG(INFO) << "Name lookup for " << name_
                 << " failed: " << ares_strerror(status);
     }
-    status_ = DNS_STATUS_ERROR;
+    status_ = DNSResolverStatus::ERROR;
     return;
   }
 
@@ -303,13 +304,13 @@ void DNSResolver::on_result(int status, hostent *hostent) {
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
     }
-    status_ = DNS_STATUS_ERROR;
+    status_ = DNSResolverStatus::ERROR;
     return;
   }
 
   switch (hostent->h_addrtype) {
   case AF_INET:
-    status_ = DNS_STATUS_OK;
+    status_ = DNSResolverStatus::OK;
     result_.len = sizeof(result_.su.in);
     result_.su.in = {};
     result_.su.in.sin_family = AF_INET;
@@ -319,7 +320,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
     memcpy(&result_.su.in.sin_addr, ap, sizeof(result_.su.in.sin_addr));
     break;
   case AF_INET6:
-    status_ = DNS_STATUS_OK;
+    status_ = DNSResolverStatus::OK;
     result_.len = sizeof(result_.su.in6);
     result_.su.in6 = {};
     result_.su.in6.sin6_family = AF_INET6;
@@ -332,7 +333,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
     assert(0);
   }
 
-  if (status_ == DNS_STATUS_OK) {
+  if (status_ == DNSResolverStatus::OK) {
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
                 << util::numeric_name(&result_.su.sa, result_.len);
@@ -340,7 +341,7 @@ void DNSResolver::on_result(int status, hostent *hostent) {
     return;
   }
 
-  status_ = DNS_STATUS_ERROR;
+  status_ = DNSResolverStatus::ERROR;
 }
 
 void DNSResolver::set_complete_cb(CompleteCb cb) {
index 268547e..696c543 100644 (file)
@@ -42,27 +42,29 @@ using namespace nghttp2;
 
 namespace shrpx {
 
-enum DNSResolverStatus {
+enum class DNSResolverStatus {
   // Resolver is in initial status
-  DNS_STATUS_IDLE,
+  IDLE,
   // Resolver is currently resolving host name
-  DNS_STATUS_RUNNING,
+  RUNNING,
   // Resolver successfully resolved host name
-  DNS_STATUS_OK,
+  OK,
   // Resolver failed to resolve host name
-  DNS_STATUS_ERROR,
+  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)>;
+// |status| is either DNSResolverStatus::OK, or
+// DNSResolverStatus::ERROR.  If |status| is DNSResolverStatus::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
+// DNSResolverStatus::RUNNING.
+using CompleteCb =
+    std::function<void(DNSResolverStatus status, const Address *result)>;
 
 // DNSResolver is asynchronous name resolver, backed by c-ares
 // library.
@@ -73,9 +75,9 @@ public:
 
   // 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;
+  // Returns status.  If status_ is DNSResolverStatus::SUCCESS &&
+  // |result| is not nullptr, |*result| is filled.
+  DNSResolverStatus 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);
@@ -108,7 +110,7 @@ private:
   // AF_INET or AF_INET6.  AF_INET for A record lookup, and AF_INET6
   // for AAAA record lookup.
   int family_;
-  int status_;
+  DNSResolverStatus status_;
 };
 
 } // namespace shrpx
index 1ab368e..67ebcb4 100644 (file)
@@ -49,7 +49,7 @@ DNSTracker::~DNSTracker() {
     while (!qlist.empty()) {
       auto head = qlist.head;
       qlist.remove(head);
-      head->status = DNS_STATUS_ERROR;
+      head->status = DNSResolverStatus::ERROR;
       head->in_qlist = false;
       // TODO Not sure we should call callback here, or it is even be
       // safe to do that.
@@ -58,7 +58,8 @@ DNSTracker::~DNSTracker() {
 }
 
 ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
-                                     ImmutableString host, int status,
+                                     ImmutableString host,
+                                     DNSResolverStatus status,
                                      const Address *result) {
   auto &dnsconf = get_config()->dns;
 
@@ -67,10 +68,12 @@ ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
   ent.host = std::move(host);
   ent.status = status;
   switch (status) {
-  case DNS_STATUS_ERROR:
-  case DNS_STATUS_OK:
+  case DNSResolverStatus::ERROR:
+  case DNSResolverStatus::OK:
     ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
     break;
+  default:
+    break;
   }
   if (result) {
     ent.result = *result;
@@ -80,23 +83,25 @@ ResolverEntry DNSTracker::make_entry(std::unique_ptr<DualDNSResolver> resolv,
 
 void DNSTracker::update_entry(ResolverEntry &ent,
                               std::unique_ptr<DualDNSResolver> resolv,
-                              int status, const Address *result) {
+                              DNSResolverStatus 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:
+  case DNSResolverStatus::ERROR:
+  case DNSResolverStatus::OK:
     ent.expiry = ev_now(loop_) + dnsconf.timeout.cache;
     break;
+  default:
+    break;
   }
   if (result) {
     ent.result = *result;
   }
 }
 
-int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
+DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
   int rv;
 
   auto it = ents_.find(dnsq->host);
@@ -106,7 +111,7 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
       LOG(INFO) << "DNS entry not found for " << dnsq->host;
     }
 
-    auto resolv = make_unique<DualDNSResolver>(loop_);
+    auto resolv = std::make_unique<DualDNSResolver>(loop_);
     auto host_copy =
         ImmutableString{std::begin(dnsq->host), std::end(dnsq->host)};
     auto host = StringRef{host_copy};
@@ -118,46 +123,41 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
       }
 
       ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
-                                     DNS_STATUS_ERROR, nullptr));
+                                     DNSResolverStatus::ERROR, nullptr));
 
       start_gc_timer();
 
-      return DNS_STATUS_ERROR;
+      return DNSResolverStatus::ERROR;
     }
 
-    rv = resolv->get_status(result);
-    switch (rv) {
-    case DNS_STATUS_ERROR: {
+    switch (resolv->get_status(result)) {
+    case DNSResolverStatus::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));
+                                     DNSResolverStatus::ERROR, nullptr));
 
       start_gc_timer();
 
-      return DNS_STATUS_ERROR;
-    }
-    case DNS_STATUS_OK: {
+      return DNSResolverStatus::ERROR;
+    case DNSResolverStatus::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));
+                                     DNSResolverStatus::OK, result));
 
       start_gc_timer();
 
-      return DNS_STATUS_OK;
-    }
-    case DNS_STATUS_RUNNING: {
-      assert(rv == DNS_STATUS_RUNNING);
-
+      return DNSResolverStatus::OK;
+    case DNSResolverStatus::RUNNING: {
       auto p = ents_.emplace(host,
                              make_entry(std::move(resolv), std::move(host_copy),
-                                        DNS_STATUS_RUNNING, nullptr));
+                                        DNSResolverStatus::RUNNING, nullptr));
 
       start_gc_timer();
 
@@ -165,7 +165,7 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
 
       add_to_qlist(ent, dnsq);
 
-      return DNS_STATUS_RUNNING;
+      return DNSResolverStatus::RUNNING;
     }
     default:
       assert(0);
@@ -174,13 +174,13 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
 
   auto &ent = (*it).second;
 
-  if (ent.status != DNS_STATUS_RUNNING && ent.expiry < ev_now(loop_)) {
+  if (ent.status != DNSResolverStatus::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 resolv = std::make_unique<DualDNSResolver>(loop_);
     auto host = StringRef{ent.host};
 
     rv = resolv->resolve(host);
@@ -189,57 +189,53 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
         LOG(INFO) << "Name lookup failed for " << host;
       }
 
-      update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
+      update_entry(ent, nullptr, DNSResolverStatus::ERROR, nullptr);
 
-      return DNS_STATUS_ERROR;
+      return DNSResolverStatus::ERROR;
     }
 
-    rv = resolv->get_status(result);
-    switch (rv) {
-    case DNS_STATUS_ERROR: {
+    switch (resolv->get_status(result)) {
+    case DNSResolverStatus::ERROR:
       if (LOG_ENABLED(INFO)) {
         LOG(INFO) << "Name lookup failed for " << host;
       }
 
-      update_entry(ent, nullptr, DNS_STATUS_ERROR, nullptr);
+      update_entry(ent, nullptr, DNSResolverStatus::ERROR, nullptr);
 
-      return DNS_STATUS_ERROR;
-    }
-    case DNS_STATUS_OK: {
+      return DNSResolverStatus::ERROR;
+    case DNSResolverStatus::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);
+      update_entry(ent, nullptr, DNSResolverStatus::OK, result);
 
-      return DNS_STATUS_OK;
-    }
-    case DNS_STATUS_RUNNING: {
-      update_entry(ent, std::move(resolv), DNS_STATUS_RUNNING, nullptr);
+      return DNSResolverStatus::OK;
+    case DNSResolverStatus::RUNNING:
+      update_entry(ent, std::move(resolv), DNSResolverStatus::RUNNING, nullptr);
       add_to_qlist(ent, dnsq);
 
-      return DNS_STATUS_RUNNING;
-    }
+      return DNSResolverStatus::RUNNING;
     default:
       assert(0);
     }
   }
 
   switch (ent.status) {
-  case DNS_STATUS_RUNNING:
+  case DNSResolverStatus::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:
+    return DNSResolverStatus::RUNNING;
+  case DNSResolverStatus::ERROR:
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "Name lookup failed for " << dnsq->host << " (cached)";
     }
-    return DNS_STATUS_ERROR;
-  case DNS_STATUS_OK:
+    return DNSResolverStatus::ERROR;
+  case DNSResolverStatus::OK:
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
                 << util::numeric_name(&ent.result.su.sa, ent.result.len);
@@ -247,7 +243,7 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
     if (result) {
       memcpy(result, &ent.result, sizeof(*result));
     }
-    return DNS_STATUS_OK;
+    return DNSResolverStatus::OK;
   default:
     assert(0);
     abort();
@@ -256,26 +252,27 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
 
 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.resolv->set_complete_cb(
+      [&ent, loop](DNSResolverStatus 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 == DNSResolverStatus::OK) {
+          ent.result = *result;
+        }
+      });
   ent.qlist.append(dnsq);
   dnsq->in_qlist = true;
 }
index 89100ea..e08247c 100644 (file)
@@ -41,7 +41,7 @@ struct DNSQuery {
         cb(std::move(cb)),
         dlnext(nullptr),
         dlprev(nullptr),
-        status(DNS_STATUS_IDLE),
+        status(DNSResolverStatus::IDLE),
         in_qlist(false) {}
 
   // Host name we lookup for.
@@ -51,7 +51,7 @@ struct DNSQuery {
   // DNSTracker::resolve().
   CompleteCb cb;
   DNSQuery *dlnext, *dlprev;
-  int status;
+  DNSResolverStatus status;
   // true if this object is in linked list ResolverEntry::qlist.
   bool in_qlist;
 };
@@ -59,13 +59,14 @@ struct DNSQuery {
 struct ResolverEntry {
   // Host name this entry lookups for.
   ImmutableString host;
-  // DNS resolver.  Only non-nullptr if status is DNS_STATUS_RUNNING.
+  // DNS resolver.  Only non-nullptr if status is
+  // DNSResolverStatus::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;
+  DNSResolverStatus status;
   // result and its expiry time
   Address result;
   // time point when cached result expires.
@@ -80,12 +81,13 @@ public:
   // 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);
+  // DNSResolverStatus::OK or DNSResolverStatus::ERROR.  If lookup is
+  // successful, DNSResolverStatus::OK is returned, and |result| is
+  // filled.  If lookup failed, DNSResolverStatus::ERROR is returned.
+  // If name lookup is being done background, it returns
+  // DNSResolverStatus::RUNNING.  Its completion is notified by
+  // calling dnsq->cb.
+  DNSResolverStatus resolve(Address *result, DNSQuery *dnsq);
   // Cancels name lookup requested by |dnsq|.
   void cancel(DNSQuery *dnsq);
   // Removes expired entries from ents_.
@@ -95,11 +97,11 @@ public:
 
 private:
   ResolverEntry make_entry(std::unique_ptr<DualDNSResolver> resolv,
-                           ImmutableString host, int status,
+                           ImmutableString host, DNSResolverStatus status,
                            const Address *result);
 
   void update_entry(ResolverEntry &ent, std::unique_ptr<DualDNSResolver> resolv,
-                    int status, const Address *result);
+                    DNSResolverStatus status, const Address *result);
 
   void add_to_qlist(ResolverEntry &ent, DNSQuery *dnsq);
 
index 2538aa9..dc7762d 100644 (file)
@@ -26,7 +26,7 @@
 
 #include <cassert>
 
-#include "http-parser/http_parser.h"
+#include "url-parser/url_parser.h"
 
 #include "shrpx_upstream.h"
 #include "shrpx_client_handler.h"
@@ -133,9 +133,9 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
       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),
+      request_state_(DownstreamState::INITIAL),
+      response_state_(DownstreamState::INITIAL),
+      dispatch_state_(DispatchState::NONE),
       upgraded_(false),
       chunked_request_(false),
       chunked_response_(false),
@@ -144,7 +144,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
       request_header_sent_(false),
       accesslog_written_(false),
       new_affinity_cookie_(false),
-      blocked_request_data_eof_(false) {
+      blocked_request_data_eof_(false),
+      expect_100_continue_(false) {
 
   auto &timeoutconf = get_config()->http2.timeout;
 
@@ -192,7 +193,7 @@ Downstream::~Downstream() {
   if (dconn_) {
     const auto &group = dconn_->get_downstream_addr_group();
     if (group) {
-      const auto &mruby_ctx = group->mruby_ctx;
+      const auto &mruby_ctx = group->shared_addr->mruby_ctx;
       mruby_ctx->delete_downstream(this);
     }
   }
@@ -230,7 +231,7 @@ void Downstream::detach_downstream_connection() {
 #ifdef HAVE_MRUBY
   const auto &group = dconn_->get_downstream_addr_group();
   if (group) {
-    const auto &mruby_ctx = group->mruby_ctx;
+    const auto &mruby_ctx = group->shared_addr->mruby_ctx;
     mruby_ctx->delete_downstream(this);
   }
 #endif // HAVE_MRUBY
@@ -255,7 +256,7 @@ std::unique_ptr<DownstreamConnection> Downstream::pop_downstream_connection() {
 
   const auto &group = dconn_->get_downstream_addr_group();
   if (group) {
-    const auto &mruby_ctx = group->mruby_ctx;
+    const auto &mruby_ctx = group->shared_addr->mruby_ctx;
     mruby_ctx->delete_downstream(this);
   }
 #endif // HAVE_MRUBY
@@ -573,6 +574,18 @@ void FieldStore::append_last_trailer_value(const char *data, size_t len) {
                                   trailers_, data, len);
 }
 
+void FieldStore::erase_content_length_and_transfer_encoding() {
+  for (auto &kv : headers_) {
+    switch (kv.token) {
+    case http2::HD_CONTENT_LENGTH:
+    case http2::HD_TRANSFER_ENCODING:
+      kv.name = StringRef{};
+      kv.token = -1;
+      break;
+    }
+  }
+}
+
 void Downstream::set_request_start_time(
     std::chrono::high_resolution_clock::time_point time) {
   request_start_time_ = std::move(time);
@@ -596,9 +609,11 @@ 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; }
+void Downstream::set_request_state(DownstreamState state) {
+  request_state_ = state;
+}
 
-int Downstream::get_request_state() const { return request_state_; }
+DownstreamState Downstream::get_request_state() const { return request_state_; }
 
 bool Downstream::get_chunked_request() const { return chunked_request_; }
 
@@ -610,7 +625,7 @@ bool Downstream::request_buf_full() {
   auto worker = handler->get_worker();
 
   // We don't check buffer size here for API endpoint.
-  if (faddr->alt_mode == ALTMODE_API) {
+  if (faddr->alt_mode == UpstreamAltMode::API) {
     return false;
   }
 
@@ -638,7 +653,7 @@ int Downstream::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_) {
+  if (!dconn_ && !request_header_sent_) {
     blocked_request_buf_.append(data, datalen);
     req_.unconsumed_body_length += datalen;
     return 0;
@@ -660,7 +675,7 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
 }
 
 int Downstream::end_upload_data() {
-  if (!request_header_sent_) {
+  if (!dconn_ && !request_header_sent_) {
     blocked_request_data_eof_ = true;
     return 0;
   }
@@ -711,9 +726,13 @@ int Downstream::on_read() {
   return dconn_->on_read();
 }
 
-void Downstream::set_response_state(int state) { response_state_ = state; }
+void Downstream::set_response_state(DownstreamState state) {
+  response_state_ = state;
+}
 
-int Downstream::get_response_state() const { return response_state_; }
+DownstreamState Downstream::get_response_state() const {
+  return response_state_;
+}
 
 DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; }
 
@@ -767,13 +786,13 @@ void Downstream::check_upgrade_fulfilled_http2() {
   // This handles nonzero req_.connect_proto and h1 frontend requests
   // WebSocket upgrade.
   upgraded_ = (req_.method == HTTP_CONNECT ||
-               req_.connect_proto == CONNECT_PROTO_WEBSOCKET) &&
+               req_.connect_proto == ConnectProto::WEBSOCKET) &&
               resp_.http_status / 100 == 2;
 }
 
 void Downstream::check_upgrade_fulfilled_http1() {
   if (req_.method == HTTP_CONNECT) {
-    if (req_.connect_proto == CONNECT_PROTO_WEBSOCKET) {
+    if (req_.connect_proto == ConnectProto::WEBSOCKET) {
       if (resp_.http_status != 101) {
         return;
       }
@@ -827,7 +846,7 @@ void Downstream::inspect_http1_request() {
         // TODO Should we check Sec-WebSocket-Key, and
         // Sec-WebSocket-Version as well?
         if (util::strieq_l("websocket", val)) {
-          req_.connect_proto = CONNECT_PROTO_WEBSOCKET;
+          req_.connect_proto = ConnectProto::WEBSOCKET;
         }
       }
     }
@@ -839,6 +858,11 @@ void Downstream::inspect_http1_request() {
       chunked_request_ = true;
     }
   }
+
+  auto expect = req_.fs.header(http2::HD_EXPECT);
+  expect_100_continue_ =
+      expect &&
+      util::strieq(expect->value, StringRef::from_lit("100-continue"));
 }
 
 void Downstream::inspect_http1_response() {
@@ -869,7 +893,7 @@ 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;
+         response_state_ == DownstreamState::INITIAL;
 }
 
 StringRef Downstream::get_http2_settings() const {
@@ -1055,15 +1079,15 @@ bool Downstream::get_request_header_sent() const {
 }
 
 bool Downstream::request_submission_ready() const {
-  return (request_state_ == Downstream::HEADER_COMPLETE ||
-          request_state_ == Downstream::MSG_COMPLETE) &&
+  return (request_state_ == DownstreamState::HEADER_COMPLETE ||
+          request_state_ == DownstreamState::MSG_COMPLETE) &&
          (request_pending_ || !request_header_sent_) &&
-         response_state_ == Downstream::INITIAL;
+         response_state_ == DownstreamState::INITIAL;
 }
 
-int Downstream::get_dispatch_state() const { return dispatch_state_; }
+DispatchState Downstream::get_dispatch_state() const { return dispatch_state_; }
 
-void Downstream::set_dispatch_state(int s) { dispatch_state_ = s; }
+void Downstream::set_dispatch_state(DispatchState s) { dispatch_state_ = s; }
 
 void Downstream::attach_blocked_link(BlockedLink *l) {
   assert(!blocked_link_);
@@ -1082,8 +1106,8 @@ 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_ &&
+  return dconn_ && response_state_ == DownstreamState::MSG_COMPLETE &&
+         request_state_ == DownstreamState::MSG_COMPLETE && !upgraded_ &&
          !resp_.connection_close && request_buf_.rleft() == 0;
 }
 
@@ -1135,6 +1159,14 @@ bool Downstream::get_blocked_request_data_eof() const {
   return blocked_request_data_eof_;
 }
 
+void Downstream::set_blocked_request_data_eof(bool f) {
+  blocked_request_data_eof_ = f;
+}
+
 void Downstream::set_ws_key(const StringRef &key) { ws_key_ = key; }
 
+bool Downstream::get_expect_100_continue() const {
+  return expect_100_continue_;
+}
+
 } // namespace shrpx
index 4ea9df3..fff49a1 100644 (file)
@@ -38,6 +38,8 @@
 
 #include <nghttp2/nghttp2.h>
 
+#include "llhttp.h"
+
 #include "shrpx_io_control.h"
 #include "shrpx_log_config.h"
 #include "http2.h"
@@ -117,6 +119,10 @@ public:
 
   bool trailer_key_prev() const { return trailer_key_prev_; }
 
+  // erase_content_length_and_transfer_encoding erases content-length
+  // and transfer-encoding header fields.
+  void erase_content_length_and_transfer_encoding();
+
   // content-length, -1 if it is unknown.
   int64_t content_length;
 
@@ -135,9 +141,9 @@ private:
 };
 
 // Protocols allowed in HTTP/2 :protocol header field.
-enum shrpx_connect_proto {
-  CONNECT_PROTO_NONE,
-  CONNECT_PROTO_WEBSOCKET,
+enum class ConnectProto {
+  NONE,
+  WEBSOCKET,
 };
 
 struct Request {
@@ -148,12 +154,13 @@ struct Request {
         method(-1),
         http_major(1),
         http_minor(1),
-        connect_proto(CONNECT_PROTO_NONE),
+        connect_proto(ConnectProto::NONE),
         upgrade_request(false),
         http2_upgrade_seen(false),
         connection_close(false),
         http2_expect_body(false),
-        no_authority(false) {}
+        no_authority(false),
+        forwarded_once(false) {}
 
   void consume(size_t len) {
     assert(unconsumed_body_length >= len);
@@ -161,10 +168,12 @@ struct Request {
   }
 
   bool regular_connect_method() const {
-    return method == HTTP_CONNECT && !connect_proto;
+    return method == HTTP_CONNECT && connect_proto == ConnectProto::NONE;
   }
 
-  bool extended_connect_method() const { return connect_proto; }
+  bool extended_connect_method() const {
+    return connect_proto != ConnectProto::NONE;
+  }
 
   FieldStore fs;
   // Timestamp when all request header fields are received.
@@ -182,6 +191,12 @@ struct Request {
   // request-target.  For HTTP/2, this is :path header field value.
   // For CONNECT request, this is empty.
   StringRef path;
+  // This is original authority which cannot be changed by per-pattern
+  // mruby script.
+  StringRef orig_authority;
+  // This is original path which cannot be changed by per-pattern
+  // mruby script.
+  StringRef orig_path;
   // the length of request body received so far
   int64_t recv_body_length;
   // The number of bytes not consumed by the application yet.
@@ -192,7 +207,7 @@ struct Request {
   // connect_proto specified in HTTP/2 :protocol pseudo header field
   // which enables extended CONNECT method.  This field is also set if
   // WebSocket upgrade is requested in h1 frontend for convenience.
-  int connect_proto;
+  ConnectProto connect_proto;
   // 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().
@@ -207,6 +222,10 @@ struct Request {
   // This happens when: For HTTP/2 request, :authority is missing.
   // For HTTP/1 request, origin or asterisk form is used.
   bool no_authority;
+  // true if backend selection is done for request once.
+  // orig_authority and orig_path have the authority and path which
+  // are used for the first backend selection.
+  bool forwarded_once;
 };
 
 struct Response {
@@ -242,7 +261,7 @@ struct Response {
   void resource_pushed(const StringRef &scheme, const StringRef &authority,
                        const StringRef &path) {
     if (!pushed_resources) {
-      pushed_resources = make_unique<
+      pushed_resources = std::make_unique<
           std::vector<std::tuple<StringRef, StringRef, StringRef>>>();
     }
     pushed_resources->emplace_back(scheme, authority, path);
@@ -274,6 +293,30 @@ struct Response {
   bool headers_only;
 };
 
+enum class DownstreamState {
+  INITIAL,
+  HEADER_COMPLETE,
+  MSG_COMPLETE,
+  STREAM_CLOSED,
+  CONNECT_FAIL,
+  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,
+};
+
+enum class DispatchState {
+  NONE,
+  PENDING,
+  BLOCKED,
+  ACTIVE,
+  FAILURE,
+};
+
 class Downstream {
 public:
   Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id);
@@ -347,24 +390,8 @@ public:
   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;
+  void set_request_state(DownstreamState state);
+  DownstreamState get_request_state() const;
   DefaultMemchunks *get_request_buf();
   void set_request_pending(bool f);
   bool get_request_pending() const;
@@ -378,6 +405,7 @@ public:
 
   DefaultMemchunks *get_blocked_request_buf();
   bool get_blocked_request_data_eof() const;
+  void set_blocked_request_data_eof(bool f);
 
   // downstream response API
   const Response &response() const { return resp_; }
@@ -389,8 +417,8 @@ public:
   bool get_chunked_response() const;
   void set_chunked_response(bool f);
 
-  void set_response_state(int state);
-  int get_response_state() const;
+  void set_response_state(DownstreamState state);
+  DownstreamState get_response_state() const;
   DefaultMemchunks *get_response_buf();
   bool response_buf_full();
   // Validates that received response body length and content-length
@@ -446,8 +474,8 @@ public:
   // true if retry attempt should not be done.
   bool no_more_retry() const;
 
-  int get_dispatch_state() const;
-  void set_dispatch_state(int s);
+  DispatchState get_dispatch_state() const;
+  void set_dispatch_state(DispatchState s);
 
   void attach_blocked_link(BlockedLink *l);
   BlockedLink *detach_blocked_link();
@@ -483,19 +511,13 @@ public:
 
   void set_ws_key(const StringRef &key);
 
+  bool get_expect_100_continue() 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
@@ -555,11 +577,11 @@ private:
   // An affinity cookie value.
   uint32_t affinity_cookie_;
   // request state
-  int request_state_;
+  DownstreamState request_state_;
   // response state
-  int response_state_;
+  DownstreamState response_state_;
   // only used by HTTP/2 upstream
-  int dispatch_state_;
+  DispatchState dispatch_state_;
   // true if the connection is upgraded (HTTP Upgrade or CONNECT),
   // excluding upgrade to HTTP/2.
   bool upgraded_;
@@ -582,6 +604,8 @@ private:
   // true if eof is received from client before sending header fields
   // to backend.
   bool blocked_request_data_eof_;
+  // true if request contains "expect: 100-continue" header field.
+  bool expect_100_continue_;
 };
 
 } // namespace shrpx
index 7446539..f8906e8 100644 (file)
@@ -49,12 +49,12 @@ DownstreamQueue::~DownstreamQueue() {
 }
 
 void DownstreamQueue::add_pending(std::unique_ptr<Downstream> downstream) {
-  downstream->set_dispatch_state(Downstream::DISPATCH_PENDING);
+  downstream->set_dispatch_state(DispatchState::PENDING);
   downstreams_.append(downstream.release());
 }
 
 void DownstreamQueue::mark_failure(Downstream *downstream) {
-  downstream->set_dispatch_state(Downstream::DISPATCH_FAILURE);
+  downstream->set_dispatch_state(DispatchState::FAILURE);
 }
 
 DownstreamQueue::HostEntry &
@@ -87,13 +87,13 @@ 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);
+  downstream->set_dispatch_state(DispatchState::ACTIVE);
 }
 
 void DownstreamQueue::mark_blocked(Downstream *downstream) {
   auto &ent = find_host_entry(make_host_key(downstream));
 
-  downstream->set_dispatch_state(Downstream::DISPATCH_BLOCKED);
+  downstream->set_dispatch_state(DispatchState::BLOCKED);
 
   auto link = new BlockedLink{};
   downstream->attach_blocked_link(link);
@@ -131,7 +131,7 @@ Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream,
   auto host = make_host_key(downstream);
   auto &ent = find_host_entry(host);
 
-  if (downstream->get_dispatch_state() == Downstream::DISPATCH_ACTIVE) {
+  if (downstream->get_dispatch_state() == DispatchState::ACTIVE) {
     --ent.num_active;
   } else {
     // For those downstreams deleted while in blocked state
index 1a75034..a5b980f 100644 (file)
@@ -67,7 +67,7 @@ public:
     size_t num_active;
   };
 
-  using HostEntryMap = std::map<StringRef, HostEntry, std::less<StringRef>>;
+  using HostEntryMap = std::map<StringRef, HostEntry>;
 
   // conn_max_per_host == 0 means no limit for downstream connection.
   DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true);
@@ -88,10 +88,10 @@ public:
   // |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.
+  // DispatchState::ACTIVE, and |next_blocked| is true, this function
+  // may return Downstream object with the same target host in
+  // DispatchState::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;
index 5f75ef4..f03eea6 100644 (file)
@@ -28,21 +28,20 @@ namespace shrpx {
 
 DualDNSResolver::DualDNSResolver(struct ev_loop *loop)
     : resolv4_(loop), resolv6_(loop) {
-  auto cb = [this](int, const Address *) {
-    int rv;
+  auto cb = [this](DNSResolverStatus, const Address *) {
     Address result;
 
-    rv = this->get_status(&result);
-    switch (rv) {
-    case DNS_STATUS_ERROR:
-    case DNS_STATUS_OK:
+    auto status = this->get_status(&result);
+    switch (status) {
+    case DNSResolverStatus::ERROR:
+    case DNSResolverStatus::OK:
       break;
     default:
       return;
     }
 
     auto cb = this->get_complete_cb();
-    cb(rv, &result);
+    cb(status, &result);
   };
 
   resolv4_.set_complete_cb(cb);
@@ -65,23 +64,22 @@ 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;
+DNSResolverStatus DualDNSResolver::get_status(Address *result) const {
+  auto rv6 = resolv6_.get_status(result);
+  if (rv6 == DNSResolverStatus::OK) {
+    return DNSResolverStatus::OK;
   }
-  rv4 = resolv4_.get_status(result);
-  if (rv4 == DNS_STATUS_OK) {
-    return DNS_STATUS_OK;
+  auto rv4 = resolv4_.get_status(result);
+  if (rv4 == DNSResolverStatus::OK) {
+    return DNSResolverStatus::OK;
   }
-  if (rv4 == DNS_STATUS_RUNNING || rv6 == DNS_STATUS_RUNNING) {
-    return DNS_STATUS_RUNNING;
+  if (rv4 == DNSResolverStatus::RUNNING || rv6 == DNSResolverStatus::RUNNING) {
+    return DNSResolverStatus::RUNNING;
   }
-  if (rv4 == DNS_STATUS_ERROR || rv6 == DNS_STATUS_ERROR) {
-    return DNS_STATUS_ERROR;
+  if (rv4 == DNSResolverStatus::ERROR || rv6 == DNSResolverStatus::ERROR) {
+    return DNSResolverStatus::ERROR;
   }
-  return DNS_STATUS_IDLE;
+  return DNSResolverStatus::IDLE;
 }
 
 } // namespace shrpx
index a8e0826..6f314ea 100644 (file)
@@ -48,7 +48,7 @@ public:
   int resolve(const StringRef &host);
   CompleteCb get_complete_cb() const;
   void set_complete_cb(CompleteCb cb);
-  int get_status(Address *result) const;
+  DNSResolverStatus get_status(Address *result) const;
 
 private:
   // For A record
index bc860b0..69bc323 100644 (file)
@@ -146,13 +146,22 @@ std::string colorizeHeaders(const char *hdrs) {
     nhdrs += TTY_HTTP_HD;
     nhdrs.append(p, np);
     nhdrs += TTY_RST;
+    auto redact = util::strieq_l("authorization", StringRef{p, np});
     p = np;
     np = strchr(p, '\n');
     if (!np) {
-      nhdrs.append(p);
+      if (redact) {
+        nhdrs.append(": <redacted>");
+      } else {
+        nhdrs.append(p);
+      }
       break;
     }
-    nhdrs.append(p, np + 1);
+    if (redact) {
+      nhdrs.append(": <redacted>\n");
+    } else {
+      nhdrs.append(p, np + 1);
+    }
     p = np + 1;
   }
   return nhdrs;
@@ -199,12 +208,12 @@ StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
   return StringRef{iov.base, p};
 }
 
-bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
+bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
                                      const StringRef &scheme) {
   switch (secure) {
-  case COOKIE_SECURE_AUTO:
+  case SessionAffinityCookieSecure::AUTO:
     return scheme == "https";
-  case COOKIE_SECURE_YES:
+  case SessionAffinityCookieSecure::YES:
     return true;
   default:
     return false;
index dfa5477..37be7e1 100644 (file)
@@ -76,7 +76,7 @@ StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
 
 // Returns true if |secure| indicates that Secure attribute should be
 // set.
-bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
+bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
                                      const StringRef &scheme);
 
 } // namespace http
index 1a4acd0..d27dcc1 100644 (file)
@@ -28,7 +28,7 @@
 #  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
-#include "http-parser/http_parser.h"
+#include "llhttp.h"
 
 #include "shrpx_client_handler.h"
 #include "shrpx_upstream.h"
@@ -62,16 +62,16 @@ Http2DownstreamConnection::~Http2DownstreamConnection() {
     downstream_->disable_downstream_wtimer();
 
     uint32_t error_code;
-    if (downstream_->get_request_state() == Downstream::STREAM_CLOSED &&
+    if (downstream_->get_request_state() == DownstreamState::STREAM_CLOSED &&
         downstream_->get_upgraded()) {
       // For upgraded connection, send NO_ERROR.  Should we consider
-      // request states other than Downstream::STREAM_CLOSED ?
+      // request states other than DownstreamState::STREAM_CLOSED ?
       error_code = NGHTTP2_NO_ERROR;
     } else {
       error_code = NGHTTP2_INTERNAL_ERROR;
     }
 
-    if (http2session_->get_state() == Http2Session::CONNECTED &&
+    if (http2session_->get_state() == Http2SessionState::CONNECTED &&
         downstream_->get_downstream_stream_id() != -1) {
       submit_rst_stream(downstream_, error_code);
 
@@ -105,7 +105,7 @@ int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
   auto &req = downstream_->request();
 
   // HTTP/2 disables HTTP Upgrade.
-  if (req.method != HTTP_CONNECT && !req.connect_proto) {
+  if (req.method != HTTP_CONNECT && req.connect_proto == ConnectProto::NONE) {
     req.upgrade_request = false;
   }
 
@@ -140,12 +140,12 @@ void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
 int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
                                                  uint32_t error_code) {
   int rv = -1;
-  if (http2session_->get_state() == Http2Session::CONNECTED &&
+  if (http2session_->get_state() == Http2SessionState::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:
+    case DownstreamState::MSG_RESET:
+    case DownstreamState::MSG_BAD_HEADER:
+    case DownstreamState::MSG_COMPLETE:
       break;
     default:
       if (LOG_ENABLED(INFO)) {
@@ -188,12 +188,12 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
   *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
 
   if (input_empty &&
-      downstream->get_request_state() == Downstream::MSG_COMPLETE &&
+      downstream->get_request_state() == DownstreamState::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_response_state() == DownstreamState::HEADER_COMPLETE &&
         !downstream->get_upgraded()))) {
 
     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
@@ -244,7 +244,8 @@ int Http2DownstreamConnection::push_request_headers() {
 
   const auto &req = downstream_->request();
 
-  if (req.connect_proto && !http2session_->get_allow_connect_proto()) {
+  if (req.connect_proto != ConnectProto::NONE &&
+      !http2session_->get_allow_connect_proto()) {
     return -1;
   }
 
@@ -292,7 +293,7 @@ int Http2DownstreamConnection::push_request_headers() {
   nva.reserve(req.fs.headers().size() + 11 + num_cookies +
               httpconf.add_request_headers.size());
 
-  if (req.connect_proto == CONNECT_PROTO_WEBSOCKET) {
+  if (req.connect_proto == ConnectProto::WEBSOCKET) {
     nva.push_back(http2::make_nv_ll(":method", "CONNECT"));
     nva.push_back(http2::make_nv_ll(":protocol", "websocket"));
   } else {
@@ -318,7 +319,7 @@ int Http2DownstreamConnection::push_request_headers() {
       nva.push_back(http2::make_nv_ls_nocopy(":path", req.path));
     }
 
-    if (!req.no_authority || req.connect_proto) {
+    if (!req.no_authority || req.connect_proto != ConnectProto::NONE) {
       nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
     } else {
       nva.push_back(http2::make_nv_ls_nocopy("host", authority));
@@ -462,6 +463,11 @@ int Http2DownstreamConnection::push_request_headers() {
   if (LOG_ENABLED(INFO)) {
     std::stringstream ss;
     for (auto &nv : nva) {
+      if (util::streq_l("authorization", nv.name, nv.namelen)) {
+        ss << TTY_HTTP_HD << StringRef{nv.name, nv.namelen} << TTY_RST
+           << ": <redacted>\n";
+        continue;
+      }
       ss << TTY_HTTP_HD << StringRef{nv.name, nv.namelen} << TTY_RST << ": "
          << StringRef{nv.value, nv.valuelen} << "\n";
     }
@@ -475,8 +481,8 @@ int Http2DownstreamConnection::push_request_headers() {
 
   // 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 || req.connect_proto || transfer_encoding ||
-      req.fs.content_length > 0 || req.http2_expect_body) {
+  if (req.method == HTTP_CONNECT || req.connect_proto != ConnectProto::NONE ||
+      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;
@@ -498,6 +504,14 @@ int Http2DownstreamConnection::push_request_headers() {
 
 int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
                                                       size_t datalen) {
+  if (!downstream_->get_request_header_sent()) {
+    auto output = downstream_->get_blocked_request_buf();
+    auto &req = downstream_->request();
+    output->append(data, datalen);
+    req.unconsumed_body_length += datalen;
+    return 0;
+  }
+
   int rv;
   auto output = downstream_->get_request_buf();
   output->append(data, datalen);
@@ -515,6 +529,11 @@ int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
 }
 
 int Http2DownstreamConnection::end_upload_data() {
+  if (!downstream_->get_request_header_sent()) {
+    downstream_->set_blocked_request_data_eof(true);
+    return 0;
+  }
+
   int rv;
   if (downstream_->get_downstream_stream_id() != -1) {
     rv = http2session_->resume_data(this);
@@ -533,7 +552,7 @@ int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
                                            size_t consumed) {
   int rv;
 
-  if (http2session_->get_state() != Http2Session::CONNECTED) {
+  if (http2session_->get_state() != Http2SessionState::CONNECTED) {
     return 0;
   }
 
index 5342a14..6043a9d 100644 (file)
@@ -69,7 +69,7 @@ void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
   ev_timer_stop(loop, w);
 
   switch (http2session->get_connection_check_state()) {
-  case Http2Session::CONNECTION_CHECK_STARTED:
+  case ConnectionCheck::STARTED:
     // ping timeout; disconnect
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, http2session) << "ping timeout";
@@ -82,8 +82,7 @@ void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, http2session) << "connection check required";
     }
-    http2session->set_connection_check_state(
-        Http2Session::CONNECTION_CHECK_REQUIRED);
+    http2session->set_connection_check_state(ConnectionCheck::REQUIRED);
   }
 }
 } // namespace
@@ -189,7 +188,7 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
             group->shared_addr->timeout.write, group->shared_addr->timeout.read,
             {}, {}, writecb, readcb, timeoutcb, this,
             get_config()->tls.dyn_rec.warmup_threshold,
-            get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP2),
+            get_config()->tls.dyn_rec.idle_timeout, Proto::HTTP2),
       wb_(worker->get_mcpool()),
       worker_(worker),
       ssl_ctx_(ssl_ctx),
@@ -197,9 +196,9 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
       addr_(addr),
       session_(nullptr),
       raddr_(nullptr),
-      state_(DISCONNECTED),
-      connection_check_state_(CONNECTION_CHECK_NONE),
-      freelist_zone_(FREELIST_ZONE_NONE),
+      state_(Http2SessionState::DISCONNECTED),
+      connection_check_state_(ConnectionCheck::NONE),
+      freelist_zone_(FreelistZone::NONE),
       settings_recved_(false),
       allow_connect_proto_(false) {
   read_ = write_ = &Http2Session::noop;
@@ -266,8 +265,8 @@ int Http2Session::disconnect(bool hard) {
     proxy_htp_.reset();
   }
 
-  connection_check_state_ = CONNECTION_CHECK_NONE;
-  state_ = DISCONNECTED;
+  connection_check_state_ = ConnectionCheck::NONE;
+  state_ = Http2SessionState::DISCONNECTED;
 
   // When deleting Http2DownstreamConnection, it calls this object's
   // remove_downstream_connection().  The multiple
@@ -305,13 +304,11 @@ int Http2Session::disconnect(bool hard) {
 }
 
 int Http2Session::resolve_name() {
-  int rv;
-
-  auto dns_query = make_unique<DNSQuery>(
-      addr_->host, [this](int status, const Address *result) {
+  auto dns_query = std::make_unique<DNSQuery>(
+      addr_->host, [this](DNSResolverStatus status, const Address *result) {
         int rv;
 
-        if (status == DNS_STATUS_OK) {
+        if (status == DNSResolverStatus::OK) {
           *resolved_addr_ = *result;
           util::set_port(*this->resolved_addr_, this->addr_->port);
         }
@@ -321,17 +318,16 @@ int Http2Session::resolve_name() {
           delete this;
         }
       });
-  resolved_addr_ = make_unique<Address>();
+  resolved_addr_ = std::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:
+  switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
+  case DNSResolverStatus::ERROR:
     return -1;
-  case DNS_STATUS_RUNNING:
+  case DNSResolverStatus::RUNNING:
     dns_query_ = std::move(dns_query);
-    state_ = RESOLVING_NAME;
+    state_ = Http2SessionState::RESOLVING_NAME;
     return 0;
-  case DNS_STATUS_OK:
+  case DNSResolverStatus::OK:
     util::set_port(*resolved_addr_, addr_->port);
     return 0;
   default:
@@ -340,12 +336,32 @@ int Http2Session::resolve_name() {
   }
 }
 
+namespace {
+int htp_hdrs_completecb(llhttp_t *htp);
+} // namespace
+
+namespace {
+constexpr llhttp_settings_t htp_hooks = {
+    nullptr,             // llhttp_cb      on_message_begin;
+    nullptr,             // llhttp_data_cb on_url;
+    nullptr,             // llhttp_data_cb on_status;
+    nullptr,             // llhttp_data_cb on_header_field;
+    nullptr,             // llhttp_data_cb on_header_value;
+    htp_hdrs_completecb, // llhttp_cb      on_headers_complete;
+    nullptr,             // llhttp_data_cb on_body;
+    nullptr,             // llhttp_cb      on_message_complete;
+    nullptr,             // llhttp_cb      on_chunk_header
+    nullptr,             // llhttp_cb      on_chunk_complete
+};
+} // namespace
+
 int Http2Session::initiate_connection() {
   int rv = 0;
 
   auto worker_blocker = worker_->get_connect_blocker();
 
-  if (state_ == DISCONNECTED || state_ == RESOLVING_NAME) {
+  if (state_ == Http2SessionState::DISCONNECTED ||
+      state_ == Http2SessionState::RESOLVING_NAME) {
     if (worker_blocker->blocked()) {
       if (LOG_ENABLED(INFO)) {
         SSLOG(INFO, this)
@@ -358,7 +374,7 @@ int Http2Session::initiate_connection() {
   auto &downstreamconf = *get_config()->conn.downstream;
 
   const auto &proxy = get_config()->downstream_http_proxy;
-  if (!proxy.host.empty() && state_ == DISCONNECTED) {
+  if (!proxy.host.empty() && state_ == Http2SessionState::DISCONNECTED) {
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, this) << "Connecting to the proxy " << proxy.host << ":"
                         << proxy.port;
@@ -405,26 +421,27 @@ int Http2Session::initiate_connection() {
     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_ = std::make_unique<llhttp_t>();
+    llhttp_init(proxy_htp_.get(), HTTP_RESPONSE, &htp_hooks);
     proxy_htp_->data = this;
 
-    state_ = PROXY_CONNECTING;
+    state_ = Http2SessionState::PROXY_CONNECTING;
 
     return 0;
   }
 
-  if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED ||
-      state_ == RESOLVING_NAME) {
+  if (state_ == Http2SessionState::DISCONNECTED ||
+      state_ == Http2SessionState::PROXY_CONNECTED ||
+      state_ == Http2SessionState::RESOLVING_NAME) {
     if (LOG_ENABLED(INFO)) {
-      if (state_ != RESOLVING_NAME) {
+      if (state_ != Http2SessionState::RESOLVING_NAME) {
         SSLOG(INFO, this) << "Connecting to downstream server";
       }
     }
     if (addr_->tls) {
       assert(ssl_ctx_);
 
-      if (state_ != RESOLVING_NAME) {
+      if (state_ != Http2SessionState::RESOLVING_NAME) {
         auto ssl = tls::create_ssl(ssl_ctx_);
         if (!ssl) {
           return -1;
@@ -452,14 +469,14 @@ int Http2Session::initiate_connection() {
         }
       }
 
-      if (state_ == DISCONNECTED) {
+      if (state_ == Http2SessionState::DISCONNECTED) {
         if (addr_->dns) {
           rv = resolve_name();
           if (rv != 0) {
             downstream_failure(addr_, nullptr);
             return -1;
           }
-          if (state_ == RESOLVING_NAME) {
+          if (state_ == Http2SessionState::RESOLVING_NAME) {
             return 0;
           }
           raddr_ = resolved_addr_.get();
@@ -468,20 +485,21 @@ int Http2Session::initiate_connection() {
         }
       }
 
-      if (state_ == RESOLVING_NAME) {
-        if (dns_query_->status == DNS_STATUS_ERROR) {
+      if (state_ == Http2SessionState::RESOLVING_NAME) {
+        if (dns_query_->status == DNSResolverStatus::ERROR) {
           downstream_failure(addr_, nullptr);
           return -1;
         }
-        assert(dns_query_->status == DNS_STATUS_OK);
-        state_ = DISCONNECTED;
+        assert(dns_query_->status == DNSResolverStatus::OK);
+        state_ = Http2SessionState::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) {
+      // If state_ == Http2SessionState::PROXY_CONNECTED, we have
+      // connected to the proxy using conn_.fd and tunnel has been
+      // established.
+      if (state_ == Http2SessionState::DISCONNECTED) {
         assert(conn_.fd == -1);
 
         conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
@@ -516,7 +534,7 @@ int Http2Session::initiate_connection() {
 
       conn_.prepare_client_handshake();
     } else {
-      if (state_ == DISCONNECTED) {
+      if (state_ == Http2SessionState::DISCONNECTED) {
         // Without TLS and proxy.
         if (addr_->dns) {
           rv = resolve_name();
@@ -524,7 +542,7 @@ int Http2Session::initiate_connection() {
             downstream_failure(addr_, nullptr);
             return -1;
           }
-          if (state_ == RESOLVING_NAME) {
+          if (state_ == Http2SessionState::RESOLVING_NAME) {
             return 0;
           }
           raddr_ = resolved_addr_.get();
@@ -533,18 +551,18 @@ int Http2Session::initiate_connection() {
         }
       }
 
-      if (state_ == RESOLVING_NAME) {
-        if (dns_query_->status == DNS_STATUS_ERROR) {
+      if (state_ == Http2SessionState::RESOLVING_NAME) {
+        if (dns_query_->status == DNSResolverStatus::ERROR) {
           downstream_failure(addr_, nullptr);
           return -1;
         }
-        assert(dns_query_->status == DNS_STATUS_OK);
-        state_ = DISCONNECTED;
+        assert(dns_query_->status == DNSResolverStatus::OK);
+        state_ = Http2SessionState::DISCONNECTED;
         dns_query_.reset();
         raddr_ = resolved_addr_.get();
       }
 
-      if (state_ == DISCONNECTED) {
+      if (state_ == Http2SessionState::DISCONNECTED) {
         // Without TLS and proxy.
         assert(conn_.fd == -1);
 
@@ -580,7 +598,7 @@ int Http2Session::initiate_connection() {
     }
 
     // We have been already connected when no TLS and proxy is used.
-    if (state_ == PROXY_CONNECTED) {
+    if (state_ == Http2SessionState::PROXY_CONNECTED) {
       on_read_ = &Http2Session::read_noop;
       on_write_ = &Http2Session::write_noop;
 
@@ -589,7 +607,7 @@ int Http2Session::initiate_connection() {
 
     write_ = &Http2Session::connected;
 
-    state_ = CONNECTING;
+    state_ = Http2SessionState::CONNECTING;
     conn_.wlimit.startw();
 
     conn_.wt.repeat = downstreamconf.timeout.connect;
@@ -605,68 +623,45 @@ int Http2Session::initiate_connection() {
 }
 
 namespace {
-int htp_hdrs_completecb(http_parser *htp) {
+int htp_hdrs_completecb(llhttp_t *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 / 100 == 2) {
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, http2session) << "Tunneling success";
     }
-    http2session->set_state(Http2Session::PROXY_CONNECTED);
+    http2session->set_state(Http2SessionState::PROXY_CONNECTED);
 
-    return 0;
+    return HPE_PAUSED;
   }
 
   SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code;
-  http2session->set_state(Http2Session::PROXY_FAILED);
+  http2session->set_state(Http2SessionState::PROXY_FAILED);
 
-  return 0;
+  return HPE_PAUSED;
 }
 } // 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());
-
+  auto htperr = llhttp_execute(proxy_htp_.get(),
+                               reinterpret_cast<const char *>(data), datalen);
   if (htperr == HPE_PAUSED) {
     switch (state_) {
-    case Http2Session::PROXY_CONNECTED:
+    case Http2SessionState::PROXY_CONNECTED:
       // Initiate SSL/TLS handshake through established tunnel.
       if (initiate_connection() != 0) {
         return -1;
       }
       return 0;
-    case Http2Session::PROXY_FAILED:
+    case Http2SessionState::PROXY_FAILED:
       return -1;
+    default:
+      break;
     }
     // should not be here
     assert(0);
@@ -726,7 +721,7 @@ void Http2Session::remove_downstream_connection(
     SSLOG(INFO, this) << "Remove downstream";
   }
 
-  if (freelist_zone_ == FREELIST_ZONE_NONE && !max_concurrency_reached()) {
+  if (freelist_zone_ == FreelistZone::NONE && !max_concurrency_reached()) {
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_
                         << ", freelist.size="
@@ -748,8 +743,8 @@ void Http2Session::remove_stream_data(StreamData *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>();
+  assert(state_ == Http2SessionState::CONNECTED);
+  auto sd = std::make_unique<StreamData>();
   sd->dlnext = sd->dlprev = nullptr;
   // TODO Specify nullptr to pri_spec for now
   auto stream_id =
@@ -768,7 +763,7 @@ int Http2Session::submit_request(Http2DownstreamConnection *dconn,
 }
 
 int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) {
-  assert(state_ == CONNECTED);
+  assert(state_ == Http2SessionState::CONNECTED);
   if (LOG_ENABLED(INFO)) {
     SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id
                       << " with error_code=" << error_code;
@@ -786,7 +781,7 @@ int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) {
 nghttp2_session *Http2Session::get_session() const { return session_; }
 
 int Http2Session::resume_data(Http2DownstreamConnection *dconn) {
-  assert(state_ == CONNECTED);
+  assert(state_ == Http2SessionState::CONNECTED);
   auto downstream = dconn->get_downstream();
   int rv = nghttp2_session_resume_data(session_,
                                        downstream->get_downstream_stream_id());
@@ -836,34 +831,34 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
     auto upstream = downstream->get_upstream();
 
     if (downstream->get_downstream_stream_id() % 2 == 0 &&
-        downstream->get_request_state() == Downstream::INITIAL) {
+        downstream->get_request_state() == DownstreamState::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);
+      downstream->set_response_state(DownstreamState::MSG_RESET);
       upstream->cancel_premature_downstream(downstream);
     } else {
-      if (downstream->get_upgraded() &&
-          downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
+      if (downstream->get_upgraded() && downstream->get_response_state() ==
+                                            DownstreamState::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);
+        downstream->set_response_state(DownstreamState::MSG_COMPLETE);
       } else if (error_code == NGHTTP2_NO_ERROR) {
         switch (downstream->get_response_state()) {
-        case Downstream::MSG_COMPLETE:
-        case Downstream::MSG_BAD_HEADER:
+        case DownstreamState::MSG_COMPLETE:
+        case DownstreamState::MSG_BAD_HEADER:
           break;
         default:
-          downstream->set_response_state(Downstream::MSG_RESET);
+          downstream->set_response_state(DownstreamState::MSG_RESET);
         }
       } else if (downstream->get_response_state() !=
-                 Downstream::MSG_BAD_HEADER) {
-        downstream->set_response_state(Downstream::MSG_RESET);
+                 DownstreamState::MSG_BAD_HEADER) {
+        downstream->set_response_state(DownstreamState::MSG_RESET);
       }
-      if (downstream->get_response_state() == Downstream::MSG_RESET &&
+      if (downstream->get_response_state() == DownstreamState::MSG_RESET &&
           downstream->get_response_rst_stream_error_code() ==
               NGHTTP2_NO_ERROR) {
         downstream->set_response_rst_stream_error_code(error_code);
@@ -1102,6 +1097,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
 
   auto status = resp.fs.header(http2::HD__STATUS);
   // libnghttp2 guarantees this exists and can be parsed
+  assert(status);
   auto status_code = http2::parse_http_status_code(status->value);
 
   resp.http_status = status_code;
@@ -1136,13 +1132,13 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
     if (rv != 0) {
       http2session->submit_rst_stream(frame->hd.stream_id,
                                       NGHTTP2_PROTOCOL_ERROR);
-      downstream->set_response_state(Downstream::MSG_RESET);
+      downstream->set_response_state(DownstreamState::MSG_RESET);
     }
 
     return 0;
   }
 
-  downstream->set_response_state(Downstream::HEADER_COMPLETE);
+  downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
   downstream->check_upgrade_fulfilled_http2();
 
   if (downstream->get_upgraded()) {
@@ -1153,7 +1149,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
       delete handler;
       return -1;
     }
-    downstream->set_request_state(Downstream::HEADER_COMPLETE);
+    downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, http2session)
           << "HTTP upgrade success. stream_id=" << frame->hd.stream_id;
@@ -1173,7 +1169,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
         resp.connection_close = true;
       } else {
         // Otherwise, use chunked encoding to keep upstream connection
-        // open.  In HTTP2, we are supporsed not to receive
+        // open.  In HTTP2, we are supposed not to receive
         // transfer-encoding.
         resp.fs.add_header_token(StringRef::from_lit("transfer-encoding"),
                                  StringRef::from_lit("chunked"), false,
@@ -1196,12 +1192,12 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
   if (rv != 0) {
     // Handling early return (in other words, response was hijacked by
     // mruby scripting).
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() == DownstreamState::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);
+      downstream->set_response_state(DownstreamState::MSG_RESET);
     }
   }
 
@@ -1228,20 +1224,21 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
     if (rv != 0) {
       http2session->submit_rst_stream(frame->hd.stream_id,
                                       NGHTTP2_INTERNAL_ERROR);
-      downstream->set_response_state(Downstream::MSG_RESET);
+      downstream->set_response_state(DownstreamState::MSG_RESET);
 
     } else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
 
       downstream->disable_downstream_rtimer();
 
-      if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
+      if (downstream->get_response_state() ==
+          DownstreamState::HEADER_COMPLETE) {
 
-        downstream->set_response_state(Downstream::MSG_COMPLETE);
+        downstream->set_response_state(DownstreamState::MSG_COMPLETE);
 
         rv = upstream->on_downstream_body_complete(downstream);
 
         if (rv != 0) {
-          downstream->set_response_state(Downstream::MSG_RESET);
+          downstream->set_response_state(DownstreamState::MSG_RESET);
         }
       }
     }
@@ -1277,15 +1274,16 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
     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);
+      if (downstream->get_response_state() ==
+          DownstreamState::HEADER_COMPLETE) {
+        downstream->set_response_state(DownstreamState::MSG_COMPLETE);
 
         auto upstream = downstream->get_upstream();
 
         rv = upstream->on_downstream_body_complete(downstream);
 
         if (rv != 0) {
-          downstream->set_response_state(Downstream::MSG_RESET);
+          downstream->set_response_state(DownstreamState::MSG_RESET);
         }
       }
     } else {
@@ -1445,7 +1443,7 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     }
 
-    downstream->set_response_state(Downstream::MSG_RESET);
+    downstream->set_response_state(DownstreamState::MSG_RESET);
   }
 
   call_downstream_readcb(http2session, downstream);
@@ -1539,8 +1537,8 @@ int on_frame_not_send_callback(nghttp2_session *session,
     return 0;
   }
 
-  // To avoid stream hanging around, flag Downstream::MSG_RESET.
-  downstream->set_response_state(Downstream::MSG_RESET);
+  // To avoid stream hanging around, flag DownstreamState::MSG_RESET.
+  downstream->set_response_state(DownstreamState::MSG_RESET);
   call_downstream_readcb(http2session, downstream);
 
   return 0;
@@ -1652,7 +1650,7 @@ nghttp2_session_callbacks *create_http2_downstream_callbacks() {
 int Http2Session::connection_made() {
   int rv;
 
-  state_ = Http2Session::CONNECTED;
+  state_ = Http2SessionState::CONNECTED;
 
   on_write_ = &Http2Session::downstream_write;
   on_read_ = &Http2Session::downstream_read;
@@ -1800,7 +1798,7 @@ int Http2Session::downstream_write() {
 
 void Http2Session::signal_write() {
   switch (state_) {
-  case Http2Session::DISCONNECTED:
+  case Http2SessionState::DISCONNECTED:
     if (!ev_is_active(&initiate_connection_timer_)) {
       if (LOG_ENABLED(INFO)) {
         LOG(INFO) << "Start connecting to backend server";
@@ -1812,9 +1810,11 @@ void Http2Session::signal_write() {
       ev_feed_event(conn_.loop, &initiate_connection_timer_, 0);
     }
     break;
-  case Http2Session::CONNECTED:
+  case Http2SessionState::CONNECTED:
     conn_.wlimit.startw();
     break;
+  default:
+    break;
   }
 }
 
@@ -1824,9 +1824,9 @@ struct ev_loop *Http2Session::get_loop() const {
 
 ev_io *Http2Session::get_wev() { return &conn_.wev; }
 
-int Http2Session::get_state() const { return state_; }
+Http2SessionState Http2Session::get_state() const { return state_; }
 
-void Http2Session::set_state(int state) { state_ = state; }
+void Http2Session::set_state(Http2SessionState state) { state_ = state; }
 
 int Http2Session::terminate_session(uint32_t error_code) {
   int rv;
@@ -1860,17 +1860,17 @@ int Http2Session::consume(int32_t stream_id, size_t len) {
 
 bool Http2Session::can_push_request(const Downstream *downstream) const {
   auto &req = downstream->request();
-  return state_ == CONNECTED &&
-         connection_check_state_ == CONNECTION_CHECK_NONE &&
-         (!req.connect_proto || settings_recved_);
+  return state_ == Http2SessionState::CONNECTED &&
+         connection_check_state_ == ConnectionCheck::NONE &&
+         (req.connect_proto == ConnectProto::NONE || settings_recved_);
 }
 
 void Http2Session::start_checking_connection() {
-  if (state_ != CONNECTED ||
-      connection_check_state_ != CONNECTION_CHECK_REQUIRED) {
+  if (state_ != Http2SessionState::CONNECTED ||
+      connection_check_state_ != ConnectionCheck::REQUIRED) {
     return;
   }
-  connection_check_state_ = CONNECTION_CHECK_STARTED;
+  connection_check_state_ = ConnectionCheck::STARTED;
 
   SSLOG(INFO, this) << "Start checking connection";
   // If connection is down, we may get error when writing data.  Issue
@@ -1889,7 +1889,7 @@ void Http2Session::reset_connection_check_timer(ev_tstamp t) {
 }
 
 void Http2Session::reset_connection_check_timer_if_not_checking() {
-  if (connection_check_state_ != CONNECTION_CHECK_NONE) {
+  if (connection_check_state_ != ConnectionCheck::NONE) {
     return;
   }
 
@@ -1899,7 +1899,7 @@ void Http2Session::reset_connection_check_timer_if_not_checking() {
 void Http2Session::connection_alive() {
   reset_connection_check_timer(CONNCHK_TIMEOUT);
 
-  if (connection_check_state_ == CONNECTION_CHECK_NONE) {
+  if (connection_check_state_ == ConnectionCheck::NONE) {
     return;
   }
 
@@ -1907,7 +1907,7 @@ void Http2Session::connection_alive() {
     SSLOG(INFO, this) << "Connection alive";
   }
 
-  connection_check_state_ = CONNECTION_CHECK_NONE;
+  connection_check_state_ = ConnectionCheck::NONE;
 
   submit_pending_requests();
 }
@@ -1922,7 +1922,7 @@ void Http2Session::submit_pending_requests() {
     }
 
     auto &req = downstream->request();
-    if (req.connect_proto && !settings_recved_) {
+    if (req.connect_proto != ConnectProto::NONE && !settings_recved_) {
       continue;
     }
 
@@ -1942,11 +1942,11 @@ void Http2Session::submit_pending_requests() {
   }
 }
 
-void Http2Session::set_connection_check_state(int state) {
+void Http2Session::set_connection_check_state(ConnectionCheck state) {
   connection_check_state_ = state;
 }
 
-int Http2Session::get_connection_check_state() const {
+ConnectionCheck Http2Session::get_connection_check_state() const {
   return connection_check_state_;
 }
 
@@ -1982,7 +1982,7 @@ int Http2Session::connected() {
   read_ = &Http2Session::read_clear;
   write_ = &Http2Session::write_clear;
 
-  if (state_ == PROXY_CONNECTING) {
+  if (state_ == Http2SessionState::PROXY_CONNECTING) {
     return do_write();
   }
 
@@ -1994,7 +1994,7 @@ int Http2Session::connected() {
   }
 
   if (connection_made() != 0) {
-    state_ = CONNECT_FAILING;
+    state_ = Http2SessionState::CONNECT_FAILING;
     return -1;
   }
 
@@ -2096,7 +2096,7 @@ int Http2Session::tls_handshake() {
   write_ = &Http2Session::write_tls;
 
   if (connection_made() != 0) {
-    state_ = CONNECT_FAILING;
+    state_ = Http2SessionState::CONNECT_FAILING;
     return -1;
   }
 
@@ -2183,10 +2183,10 @@ int Http2Session::write_void() {
 
 bool Http2Session::should_hard_fail() const {
   switch (state_) {
-  case PROXY_CONNECTING:
-  case PROXY_FAILED:
+  case Http2SessionState::PROXY_CONNECTING:
+  case Http2SessionState::PROXY_FAILED:
     return true;
-  case DISCONNECTED: {
+  case Http2SessionState::DISCONNECTED: {
     const auto &proxy = get_config()->downstream_http_proxy;
     return !proxy.host.empty();
   }
@@ -2215,7 +2215,7 @@ int Http2Session::handle_downstream_push_promise(Downstream *downstream,
 
   auto handler = upstream->get_client_handler();
 
-  auto promised_dconn = make_unique<Http2DownstreamConnection>(this);
+  auto promised_dconn = std::make_unique<Http2DownstreamConnection>(this);
   promised_dconn->set_client_handler(handler);
 
   auto ptr = promised_dconn.get();
@@ -2225,7 +2225,7 @@ int Http2Session::handle_downstream_push_promise(Downstream *downstream,
     return -1;
   }
 
-  auto promised_sd = make_unique<StreamData>();
+  auto promised_sd = std::make_unique<StreamData>();
 
   nghttp2_session_set_stream_user_data(session_, promised_stream_id,
                                        promised_sd.get());
@@ -2282,7 +2282,7 @@ int Http2Session::handle_downstream_push_promise_complete(
 
   auto upstream = promised_downstream->get_upstream();
 
-  promised_downstream->set_request_state(Downstream::MSG_COMPLETE);
+  promised_downstream->set_request_state(DownstreamState::MSG_COMPLETE);
   promised_downstream->set_request_header_sent(true);
 
   if (upstream->on_downstream_push_promise_complete(downstream,
@@ -2313,24 +2313,8 @@ 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) {
+  if (freelist_zone_ != FreelistZone::NONE) {
     return;
   }
 
@@ -2340,24 +2324,15 @@ void Http2Session::add_to_extra_freelist() {
                       << addr_->http2_extra_freelist.size();
   }
 
-  freelist_zone_ = FREELIST_ZONE_EXTRA;
+  freelist_zone_ = FreelistZone::EXTRA;
   addr_->http2_extra_freelist.append(this);
 }
 
 void Http2Session::remove_from_freelist() {
   switch (freelist_zone_) {
-  case FREELIST_ZONE_NONE:
+  case FreelistZone::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:
+  case FreelistZone::EXTRA:
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, this) << "Remove from http2_extra_freelist, addr=" << addr_
                         << ", freelist.size="
@@ -2365,34 +2340,35 @@ void Http2Session::remove_from_freelist() {
     }
     addr_->http2_extra_freelist.remove(this);
     break;
-  case FREELIST_ZONE_GONE:
+  case FreelistZone::GONE:
     return;
   }
 
-  freelist_zone_ = FREELIST_ZONE_NONE;
+  freelist_zone_ = FreelistZone::NONE;
 }
 
 void Http2Session::exclude_from_scheduling() {
   remove_from_freelist();
-  freelist_zone_ = FREELIST_ZONE_GONE;
+  freelist_zone_ = FreelistZone::GONE;
 }
 
 DefaultMemchunks *Http2Session::get_request_buf() { return &wb_; }
 
 void Http2Session::on_timeout() {
   switch (state_) {
-  case PROXY_CONNECTING: {
+  case Http2SessionState::PROXY_CONNECTING: {
     auto worker_blocker = worker_->get_connect_blocker();
     worker_blocker->on_failure();
     break;
   }
-  case CONNECTING: {
+  case Http2SessionState::CONNECTING:
     SSLOG(WARN, this) << "Connect time out; addr="
                       << util::to_numeric_addr(raddr_);
 
     downstream_failure(addr_, raddr_);
     break;
-  }
+  default:
+    break;
   }
 }
 
index 09b865b..31b2545 100644 (file)
@@ -36,7 +36,7 @@
 
 #include <nghttp2/nghttp2.h>
 
-#include "http-parser/http_parser.h"
+#include "llhttp.h"
 
 #include "shrpx_connection.h"
 #include "buffer.h"
@@ -58,18 +58,43 @@ struct StreamData {
   Http2DownstreamConnection *dconn;
 };
 
-enum FreelistZone {
+enum class 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,
+  NONE,
   // Http2Session object is linked in address scope
   // http2_extra_freelist.
-  FREELIST_ZONE_EXTRA,
+  EXTRA,
   // Http2Session object is about to be deleted, and it does not
   // belong to any linked list.
-  FREELIST_ZONE_GONE
+  GONE
+};
+
+enum class Http2SessionState {
+  // 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 class ConnectionCheck {
+  // Connection checking is not required
+  NONE,
+  // Connection checking is required
+  REQUIRED,
+  // Connection checking has been started
+  STARTED,
 };
 
 class Http2Session {
@@ -135,8 +160,8 @@ public:
 
   ev_io *get_wev();
 
-  int get_state() const;
-  void set_state(int state);
+  Http2SessionState get_state() const;
+  void set_state(Http2SessionState state);
 
   void start_settings_timer();
   void stop_settings_timer();
@@ -159,8 +184,8 @@ public:
   // 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;
+  void set_connection_check_state(ConnectionCheck state);
+  ConnectionCheck get_connection_check_state() const;
 
   bool should_hard_fail() const;
 
@@ -218,34 +243,6 @@ public:
 
   bool get_allow_connect_proto() 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;
@@ -255,7 +252,7 @@ private:
   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_state_ = ConnectionCheck::REQUIRED.  After
   // connection check has started, this timer is started again and
   // traps PING ACK timeout.
   ev_timer connchk_timer_;
@@ -268,7 +265,7 @@ private:
   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_;
+  std::unique_ptr<llhttp_t> proxy_htp_;
   Worker *worker_;
   // NULL if no TLS is configured
   SSL_CTX *ssl_ctx_;
@@ -283,9 +280,9 @@ private:
   // 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_;
+  Http2SessionState state_;
+  ConnectionCheck connection_check_state_;
+  FreelistZone freelist_zone_;
   // true if SETTINGS without ACK is received from peer.
   bool settings_recved_;
   // true if peer enables RFC 8441 CONNECT protocol.
index ac0b943..9b9e552 100644 (file)
@@ -77,7 +77,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
 
   req.unconsumed_body_length = 0;
 
-  if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
+  if (downstream->get_request_state() == DownstreamState::CONNECT_FAIL) {
     upstream->remove_downstream(downstream);
     // downstream was deleted
 
@@ -89,7 +89,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
     downstream->detach_downstream_connection();
   }
 
-  downstream->set_request_state(Downstream::STREAM_CLOSED);
+  downstream->set_request_state(DownstreamState::STREAM_CLOSED);
 
   // At this point, downstream read may be paused.
 
@@ -187,7 +187,7 @@ int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
   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) {
+    if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       return 0;
     }
 
@@ -278,8 +278,8 @@ int on_begin_headers_callback(nghttp2_session *session,
 } // namespace
 
 void Http2Upstream::on_start_request(const nghttp2_frame *frame) {
-  auto downstream = make_unique<Downstream>(this, handler_->get_mcpool(),
-                                            frame->hd.stream_id);
+  auto downstream = std::make_unique<Downstream>(this, handler_->get_mcpool(),
+                                                 frame->hd.stream_id);
   nghttp2_session_set_stream_user_data(session_, frame->hd.stream_id,
                                        downstream.get());
 
@@ -312,7 +312,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
   auto &req = downstream->request();
   req.tstamp = lgconf->tstamp;
 
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
     return 0;
   }
 
@@ -321,6 +321,10 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
   if (LOG_ENABLED(INFO)) {
     std::stringstream ss;
     for (auto &nv : nva) {
+      if (nv.name == "authorization") {
+        ss << TTY_HTTP_HD << nv.name << TTY_RST << ": <redacted>\n";
+        continue;
+      }
       ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
     }
     ULOG(INFO, this) << "HTTP request headers. stream_id="
@@ -358,8 +362,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
   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) {
+  if (method_token != HTTP_CONNECT && config->http2_proxy &&
+      faddr->alt_mode == UpstreamAltMode::NONE && !authority) {
     rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
     return 0;
   }
@@ -383,7 +387,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
     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) {
+    } else if (config->http2_proxy &&
+               faddr->alt_mode == UpstreamAltMode::NONE) {
       req.path = path->value;
     } else {
       req.path = http2::rewrite_clean_path(downstream->get_block_allocator(),
@@ -399,7 +404,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
       }
       return 0;
     }
-    req.connect_proto = CONNECT_PROTO_WEBSOCKET;
+    req.connect_proto = ConnectProto::WEBSOCKET;
   }
 
   if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
@@ -412,7 +417,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
 
   downstream->inspect_http2_request();
 
-  downstream->set_request_state(Downstream::HEADER_COMPLETE);
+  downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
 
 #ifdef HAVE_MRUBY
   auto upstream = downstream->get_upstream();
@@ -431,10 +436,10 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
   if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
     downstream->disable_upstream_rtimer();
 
-    downstream->set_request_state(Downstream::MSG_COMPLETE);
+    downstream->set_request_state(DownstreamState::MSG_COMPLETE);
   }
 
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
     return 0;
   }
 
@@ -455,44 +460,39 @@ void Http2Upstream::start_downstream(Downstream *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);
-    }
+  DownstreamConnection *dconn_ptr;
 
-    downstream->set_request_state(Downstream::CONNECT_FAIL);
-    downstream_queue_.mark_failure(downstream);
+  for (;;) {
+    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);
+      }
 
-    return;
-  }
+      downstream->set_request_state(DownstreamState::CONNECT_FAIL);
+      downstream_queue_.mark_failure(downstream);
+
+      return;
+    }
 
 #ifdef HAVE_MRUBY
-  auto dconn_ptr = dconn.get();
+    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);
+    rv = downstream->attach_downstream_connection(std::move(dconn));
+    if (rv == 0) {
+      break;
     }
-
-    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;
+    const auto &mruby_ctx = group->shared_addr->mruby_ctx;
     if (mruby_ctx->run_on_request_proc(downstream) != 0) {
       if (error_reply(downstream, 500) != 0) {
         rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
@@ -503,7 +503,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
       return;
     }
 
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       return;
     }
   }
@@ -555,12 +555,12 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
       downstream->disable_upstream_rtimer();
 
       if (downstream->end_upload_data() != 0) {
-        if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
+        if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
           upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
         }
       }
 
-      downstream->set_request_state(Downstream::MSG_COMPLETE);
+      downstream->set_request_state(DownstreamState::MSG_COMPLETE);
     }
 
     return 0;
@@ -584,12 +584,12 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
       downstream->disable_upstream_rtimer();
 
       if (downstream->end_upload_data() != 0) {
-        if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
+        if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
           upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
         }
       }
 
-      downstream->set_request_state(Downstream::MSG_COMPLETE);
+      downstream->set_request_state(DownstreamState::MSG_COMPLETE);
     }
 
     return 0;
@@ -636,7 +636,7 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
   downstream->reset_upstream_rtimer();
 
   if (downstream->push_upload_data_chunk(data, len) != 0) {
-    if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
       upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
     }
 
@@ -705,7 +705,7 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
       return 0;
     }
 
-    auto promised_downstream = make_unique<Downstream>(
+    auto promised_downstream = std::make_unique<Downstream>(
         upstream, handler->get_mcpool(), promised_stream_id);
     auto &req = promised_downstream->request();
 
@@ -756,7 +756,7 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
 
     promised_downstream->inspect_http2_request();
 
-    promised_downstream->set_request_state(Downstream::MSG_COMPLETE);
+    promised_downstream->set_request_state(DownstreamState::MSG_COMPLETE);
 
     // a bit weird but start_downstream() expects that given
     // downstream is in pending queue.
@@ -1026,10 +1026,11 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
 
   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);
+  rv =
+      nghttp2_session_server_new2(&session_, http2conf.upstream.callbacks, this,
+                                  faddr->alt_mode != UpstreamAltMode::NONE
+                                      ? http2conf.upstream.alt_mode_option
+                                      : http2conf.upstream.option);
 
   assert(rv == 0);
 
@@ -1043,7 +1044,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
   entry[0].value = http2conf.upstream.max_concurrent_streams;
 
   entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
-  if (faddr->alt_mode) {
+  if (faddr->alt_mode != UpstreamAltMode::NONE) {
     entry[1].value = (1u << 31) - 1;
   } else {
     entry[1].value = http2conf.upstream.window_size;
@@ -1070,7 +1071,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
   }
 
   auto window_size =
-      faddr->alt_mode
+      faddr->alt_mode != UpstreamAltMode::NONE
           ? std::numeric_limits<int32_t>::max()
           : http2conf.upstream.optimize_window_size
                 ? std::min(http2conf.upstream.connection_window_size,
@@ -1186,7 +1187,7 @@ int Http2Upstream::on_write() {
 
       if (http2conf.upstream.optimize_window_size) {
         auto faddr = handler_->get_upstream_addr();
-        if (!faddr->alt_mode) {
+        if (faddr->alt_mode == UpstreamAltMode::NONE) {
           auto window_size = std::min(http2conf.upstream.connection_window_size,
                                       static_cast<int32_t>(hint.rwin * 2));
 
@@ -1239,7 +1240,7 @@ 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) {
+  if (downstream->get_response_state() == DownstreamState::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
@@ -1250,7 +1251,8 @@ int Http2Upstream::downstream_read(DownstreamConnection *dconn) {
     downstream->pop_downstream_connection();
     // dconn was deleted
     dconn = nullptr;
-  } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
+  } else if (downstream->get_response_state() ==
+             DownstreamState::MSG_BAD_HEADER) {
     if (error_reply(downstream, 502) != 0) {
       return -1;
     }
@@ -1260,7 +1262,10 @@ int Http2Upstream::downstream_read(DownstreamConnection *dconn) {
   } else {
     auto rv = downstream->on_read();
     if (rv == SHRPX_ERR_EOF) {
-      return downstream_eof(dconn);
+      if (downstream->get_request_header_sent()) {
+        return downstream_eof(dconn);
+      }
+      return SHRPX_ERR_RETRY;
     }
     if (rv == SHRPX_ERR_DCONN_CANCELED) {
       downstream->pop_downstream_connection();
@@ -1314,19 +1319,20 @@ int Http2Upstream::downstream_eof(DownstreamConnection *dconn) {
   // dconn was deleted
   dconn = nullptr;
   // downstream wil be deleted in on_stream_close_callback.
-  if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::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);
+    downstream->set_response_state(DownstreamState::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) {
+  } else if (downstream->get_response_state() !=
+             DownstreamState::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) {
@@ -1358,7 +1364,7 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
   // dconn was deleted
   dconn = nullptr;
 
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::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.
@@ -1366,7 +1372,7 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
       rst_stream(downstream, NGHTTP2_NO_ERROR);
     }
   } else {
-    if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
+    if (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE) {
       if (downstream->get_upgraded()) {
         on_downstream_body_complete(downstream);
       } else {
@@ -1375,7 +1381,11 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
     } else {
       unsigned int status;
       if (events & Downstream::EVENT_TIMEOUT) {
-        status = 504;
+        if (downstream->get_request_header_sent()) {
+          status = 504;
+        } else {
+          status = 408;
+        }
       } else {
         status = 502;
       }
@@ -1383,7 +1393,7 @@ int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) {
         return -1;
       }
     }
-    downstream->set_response_state(Downstream::MSG_COMPLETE);
+    downstream->set_response_state(DownstreamState::MSG_COMPLETE);
   }
   handler_->signal_write();
   // At this point, downstream may be deleted.
@@ -1450,7 +1460,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
   *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
 
   if (body_empty &&
-      downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+      downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
 
     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
 
@@ -1547,7 +1557,7 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
 
   buf->append(body, bodylen);
 
-  downstream->set_response_state(Downstream::MSG_COMPLETE);
+  downstream->set_response_state(DownstreamState::MSG_COMPLETE);
 
   if (data_prd_ptr) {
     downstream->reset_upstream_wtimer();
@@ -1567,7 +1577,7 @@ int Http2Upstream::error_reply(Downstream *downstream,
   resp.http_status = status_code;
   auto body = downstream->get_response_buf();
   body->append(html);
-  downstream->set_response_state(Downstream::MSG_COMPLETE);
+  downstream->set_response_state(DownstreamState::MSG_COMPLETE);
 
   nghttp2_data_provider data_prd;
   data_prd.source.ptr = downstream;
@@ -1655,7 +1665,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
     auto dconn = downstream->get_downstream_connection();
     const auto &group = dconn->get_downstream_addr_group();
     if (group) {
-      const auto &dmruby_ctx = group->mruby_ctx;
+      const auto &dmruby_ctx = group->shared_addr->mruby_ctx;
 
       if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
         if (error_reply(downstream, 500) != 0) {
@@ -1665,7 +1675,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
         return -1;
       }
 
-      if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+      if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
         return -1;
       }
     }
@@ -1681,7 +1691,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
       return -1;
     }
 
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       return -1;
     }
   }
@@ -1750,7 +1760,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
   auto striphd_flags = http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA;
   StringRef response_status;
 
-  if (req.connect_proto == CONNECT_PROTO_WEBSOCKET && resp.http_status == 101) {
+  if (req.connect_proto == ConnectProto::WEBSOCKET && resp.http_status == 101) {
     response_status = http2::stringify_status(balloc, 200);
     striphd_flags |= http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT;
   } else {
@@ -1980,7 +1990,7 @@ int Http2Upstream::consume(int32_t stream_id, size_t len) {
 
   auto faddr = handler_->get_upstream_addr();
 
-  if (faddr->alt_mode) {
+  if (faddr->alt_mode != UpstreamAltMode::NONE) {
     return 0;
   }
 
@@ -2021,7 +2031,7 @@ int Http2Upstream::on_timeout(Downstream *downstream) {
 
 void Http2Upstream::on_handler_delete() {
   for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) {
-    if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE &&
+    if (d->get_dispatch_state() == DispatchState::ACTIVE &&
         d->accesslog_ready()) {
       handler_->write_accesslog(d);
     }
@@ -2031,10 +2041,10 @@ void Http2Upstream::on_handler_delete() {
 int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
   int rv;
 
-  if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
+  if (downstream->get_dispatch_state() != DispatchState::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
+    // DispatchState::ACTIVE state, or we did not set
     // DownstreamConnection.
     downstream->pop_downstream_connection();
     handler_->signal_write();
@@ -2043,7 +2053,7 @@ int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
   }
 
   if (!downstream->request_submission_ready()) {
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       // We have got all response body already.  Send it off.
       downstream->pop_downstream_connection();
       return 0;
@@ -2072,14 +2082,16 @@ int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
   // downstream connection is clean; we can retry with new
   // downstream connection.
 
-  dconn = handler_->get_downstream_connection(rv, downstream);
-  if (!dconn) {
-    goto fail;
-  }
+  for (;;) {
+    auto 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->attach_downstream_connection(std::move(dconn));
+    if (rv == 0) {
+      break;
+    }
   }
 
   rv = downstream->push_request_headers();
@@ -2295,7 +2307,7 @@ Http2Upstream::on_downstream_push_promise(Downstream *downstream,
   // promised_stream_id is for backend HTTP/2 session, not for
   // frontend.
   auto promised_downstream =
-      make_unique<Downstream>(this, handler_->get_mcpool(), 0);
+      std::make_unique<Downstream>(this, handler_->get_mcpool(), 0);
   auto &promised_req = promised_downstream->request();
 
   promised_downstream->set_downstream_stream_id(promised_stream_id);
index 31f48a2..f68cfcf 100644 (file)
@@ -78,6 +78,8 @@ void retry_downstream_connection(Downstream *downstream,
   auto upstream = downstream->get_upstream();
   auto handler = upstream->get_client_handler();
 
+  assert(!downstream->get_request_header_sent());
+
   downstream->add_retry();
 
   if (downstream->no_more_retry()) {
@@ -86,21 +88,25 @@ void retry_downstream_connection(Downstream *downstream,
   }
 
   downstream->pop_downstream_connection();
+  auto buf = downstream->get_request_buf();
+  buf->reset();
 
   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) {
+
+  for (;;) {
+    auto ndconn = handler->get_downstream_connection(rv, downstream);
+    if (!ndconn) {
+      break;
+    }
+    if (downstream->attach_downstream_connection(std::move(ndconn)) != 0) {
+      continue;
+    }
+    if (downstream->push_request_headers() == 0) {
       return;
     }
   }
 
-  downstream->set_request_state(Downstream::CONNECT_FAIL);
+  downstream->set_request_state(DownstreamState::CONNECT_FAIL);
 
   if (rv == SHRPX_ERR_TLS_REQUIRED) {
     rv = upstream->on_downstream_abort_request_with_https_redirect(downstream);
@@ -133,26 +139,33 @@ void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
 } // namespace
 
 namespace {
+void backend_retry(Downstream *downstream) {
+  retry_downstream_connection(downstream, 502);
+}
+} // namespace
+
+namespace {
 void readcb(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();
 
-  if (upstream->downstream_read(dconn) != 0) {
+  rv = upstream->downstream_read(dconn);
+  if (rv != 0) {
+    if (rv == SHRPX_ERR_RETRY) {
+      backend_retry(downstream);
+      return;
+    }
+
     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);
@@ -187,26 +200,26 @@ void connectcb(struct ev_loop *loop, ev_io *w, int revents) {
 } // namespace
 
 HttpDownstreamConnection::HttpDownstreamConnection(
-    const std::shared_ptr<DownstreamAddrGroup> &group, size_t initial_addr_idx,
+    const std::shared_ptr<DownstreamAddrGroup> &group, DownstreamAddr *addr,
     struct ev_loop *loop, Worker *worker)
     : conn_(loop, -1, nullptr, worker->get_mcpool(),
             group->shared_addr->timeout.write, group->shared_addr->timeout.read,
             {}, {}, connectcb, readcb, connect_timeoutcb, this,
             get_config()->tls.dyn_rec.warmup_threshold,
-            get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP1),
+            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),
+      addr_(addr),
       raddr_(nullptr),
       ioctrl_(&conn_.rlimit),
       response_htp_{0},
-      initial_addr_idx_(initial_addr_idx),
-      reuse_first_write_done_(true),
-      reusable_(true) {}
+      first_write_done_(false),
+      reusable_(true),
+      request_header_written_(false) {}
 
 HttpDownstreamConnection::~HttpDownstreamConnection() {
   if (LOG_ENABLED(INFO)) {
@@ -237,6 +250,30 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
   return 0;
 }
 
+namespace {
+int htp_msg_begincb(llhttp_t *htp);
+int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len);
+int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len);
+int htp_hdrs_completecb(llhttp_t *htp);
+int htp_bodycb(llhttp_t *htp, const char *data, size_t len);
+int htp_msg_completecb(llhttp_t *htp);
+} // namespace
+
+namespace {
+constexpr llhttp_settings_t htp_hooks = {
+    htp_msg_begincb,     // llhttp_cb      on_message_begin;
+    nullptr,             // llhttp_data_cb on_url;
+    nullptr,             // llhttp_data_cb on_status;
+    htp_hdr_keycb,       // llhttp_data_cb on_header_field;
+    htp_hdr_valcb,       // llhttp_data_cb on_header_value;
+    htp_hdrs_completecb, // llhttp_cb      on_headers_complete;
+    htp_bodycb,          // llhttp_data_cb on_body;
+    htp_msg_completecb,  // llhttp_cb      on_message_complete;
+    nullptr,             // llhttp_cb      on_chunk_header
+    nullptr,             // llhttp_cb      on_chunk_complete
+};
+} // namespace
+
 int HttpDownstreamConnection::initiate_connection() {
   int rv;
 
@@ -252,206 +289,144 @@ int HttpDownstreamConnection::initiate_connection() {
   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;
-          }
-        }
+    auto check_dns_result = dns_query_.get() != nullptr;
 
-        if (addr->proto != PROTO_HTTP1) {
-          if (end == next_downstream) {
-            return SHRPX_ERR_NETWORK;
-          }
+    if (check_dns_result) {
+      assert(addr_->dns);
+    }
 
-          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";
       }
 
-      auto &connect_blocker = addr->connect_blocker;
+      return SHRPX_ERR_NETWORK;
+    }
 
-      if (connect_blocker->blocked()) {
-        if (LOG_ENABLED(INFO)) {
-          DCLOG(INFO, this) << "Backend server " << addr->host << ":"
-                            << addr->port << " was not available temporarily";
-        }
+    Address *raddr;
 
-        if (check_dns_result) {
-          dns_query_.reset();
-        } else if (end == next_downstream) {
-          return SHRPX_ERR_NETWORK;
-        }
+    if (addr_->dns) {
+      if (!check_dns_result) {
+        auto dns_query = std::make_unique<DNSQuery>(
+            addr_->host,
+            [this](DNSResolverStatus status, const Address *result) {
+              int rv;
 
-        continue;
-      }
+              if (status == DNSResolverStatus::OK) {
+                *this->resolved_addr_ = *result;
+              }
 
-      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);
-          }
-        }
+              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();
 
-        raddr = resolved_addr_.get();
-        util::set_port(*resolved_addr_, addr->port);
+        if (!resolved_addr_) {
+          resolved_addr_ = std::make_unique<Address>();
+        }
+        switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
+        case DNSResolverStatus::ERROR:
+          downstream_failure(addr_, nullptr);
+          return SHRPX_ERR_NETWORK;
+        case DNSResolverStatus::RUNNING:
+          dns_query_ = std::move(dns_query);
+          return 0;
+        case DNSResolverStatus::OK:
+          break;
+        default:
+          assert(0);
+        }
       } else {
-        raddr = &addr->addr;
+        switch (dns_query_->status) {
+        case DNSResolverStatus::ERROR:
+          dns_query_.reset();
+          downstream_failure(addr_, nullptr);
+          return SHRPX_ERR_NETWORK;
+        case DNSResolverStatus::OK:
+          dns_query_.reset();
+          break;
+        default:
+          assert(0);
+        }
       }
 
-      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;
+      raddr = resolved_addr_.get();
+      util::set_port(*resolved_addr_, addr_->port);
+    } else {
+      raddr = &addr_->addr;
+    }
 
-        worker_blocker->on_failure();
+    conn_.fd = util::create_nonblock_socket(raddr->su.storage.ss_family);
 
-        return SHRPX_ERR_NETWORK;
-      }
+    if (conn_.fd == -1) {
+      auto error = errno;
+      DCLOG(WARN, this) << "socket() failed; addr="
+                        << util::to_numeric_addr(raddr) << ", errno=" << error;
 
-      worker_blocker->on_success();
+      worker_blocker->on_failure();
 
-      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;
+      return SHRPX_ERR_NETWORK;
+    }
 
-        downstream_failure(addr, raddr);
+    worker_blocker->on_success();
 
-        close(conn_.fd);
-        conn_.fd = -1;
+    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;
 
-        if (!check_dns_result && end == next_downstream) {
-          return SHRPX_ERR_NETWORK;
-        }
+      downstream_failure(addr_, raddr);
 
-        // Try again with the next downstream server
-        continue;
-      }
-
-      if (LOG_ENABLED(INFO)) {
-        DCLOG(INFO, this) << "Connecting to downstream server";
-      }
+      return SHRPX_ERR_NETWORK;
+    }
 
-      addr_ = addr;
-      raddr_ = raddr;
+    if (LOG_ENABLED(INFO)) {
+      DCLOG(INFO, this) << "Connecting to downstream server";
+    }
 
-      if (addr_->tls) {
-        assert(ssl_ctx_);
+    raddr_ = raddr;
 
-        auto ssl = tls::create_ssl(ssl_ctx_);
-        if (!ssl) {
-          return -1;
-        }
+    if (addr_->tls) {
+      assert(ssl_ctx_);
 
-        tls::setup_downstream_http1_alpn(ssl);
+      auto ssl = tls::create_ssl(ssl_ctx_);
+      if (!ssl) {
+        return -1;
+      }
 
-        conn_.set_ssl(ssl);
-        conn_.tls.client_session_cache = &addr_->tls_session_cache;
+      tls::setup_downstream_http1_alpn(ssl);
 
-        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());
-        }
+      conn_.set_ssl(ssl);
+      conn_.tls.client_session_cache = &addr_->tls_session_cache;
 
-        auto session = tls::reuse_tls_session(addr_->tls_session_cache);
-        if (session) {
-          SSL_set_session(conn_.tls.ssl, session);
-          SSL_SESSION_free(session);
-        }
+      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());
+      }
 
-        conn_.prepare_client_handshake();
+      auto session = tls::reuse_tls_session(addr_->tls_session_cache);
+      if (session) {
+        SSL_set_session(conn_.tls.ssl, session);
+        SSL_SESSION_free(session);
       }
 
-      ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
-      ev_io_set(&conn_.rev, conn_.fd, EV_READ);
+      conn_.prepare_client_handshake();
+    }
 
-      conn_.wlimit.startw();
+    ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
+    ev_io_set(&conn_.rev, conn_.fd, EV_READ);
 
-      break;
-    }
+    conn_.wlimit.startw();
 
     conn_.wt.repeat = downstreamconf.timeout.connect;
     ev_timer_again(conn_.loop, &conn_.wt);
@@ -467,18 +442,19 @@ int HttpDownstreamConnection::initiate_connection() {
 
     ev_set_cb(&conn_.rev, readcb);
 
-    on_write_ = &HttpDownstreamConnection::write_reuse_first;
-    reuse_first_write_done_ = false;
+    on_write_ = &HttpDownstreamConnection::write_first;
+    first_write_done_ = false;
+    request_header_written_ = false;
   }
 
-  http_parser_init(&response_htp_, HTTP_RESPONSE);
+  llhttp_init(&response_htp_, HTTP_RESPONSE, &htp_hooks);
   response_htp_.data = downstream_;
 
   return 0;
 }
 
 int HttpDownstreamConnection::push_request_headers() {
-  if (downstream_->get_request_header_sent()) {
+  if (request_header_written_) {
     signal_write();
     return 0;
   }
@@ -493,9 +469,7 @@ int HttpDownstreamConnection::push_request_headers() {
   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);
+  request_header_written_ = true;
 
   // For HTTP/1.0 request, there is no authority in request.  In that
   // case, we use backend server's host nonetheless.
@@ -513,7 +487,7 @@ int HttpDownstreamConnection::push_request_headers() {
 
   // Assume that method and request path do not contain \r\n.
   auto meth = http2::to_method_string(
-      req.connect_proto == CONNECT_PROTO_WEBSOCKET ? HTTP_GET : req.method);
+      req.connect_proto == ConnectProto::WEBSOCKET ? HTTP_GET : req.method);
   buf->append(meth);
   buf->append(' ');
 
@@ -566,7 +540,7 @@ int HttpDownstreamConnection::push_request_headers() {
     buf->append("Transfer-Encoding: chunked\r\n");
   }
 
-  if (req.connect_proto == CONNECT_PROTO_WEBSOCKET) {
+  if (req.connect_proto == ConnectProto::WEBSOCKET) {
     if (req.http_major == 2) {
       std::array<uint8_t, 16> nonce;
       util::random_bytes(std::begin(nonce), std::end(nonce),
@@ -727,11 +701,12 @@ int HttpDownstreamConnection::push_request_headers() {
   // enables us to send headers and data in one writev system call.
   if (req.method == HTTP_CONNECT ||
       downstream_->get_blocked_request_buf()->rleft() ||
-      (!req.http2_expect_body && req.fs.content_length == 0)) {
+      (!req.http2_expect_body && req.fs.content_length == 0) ||
+      downstream_->get_expect_100_continue()) {
     signal_write();
   }
 
-  return process_blocked_request_buf();
+  return 0;
 }
 
 int HttpDownstreamConnection::process_blocked_request_buf() {
@@ -746,15 +721,16 @@ int HttpDownstreamConnection::process_blocked_request_buf() {
       dest->append("\r\n");
     }
 
-    src->remove(*dest);
+    src->copy(*dest);
 
     if (chunked) {
       dest->append("\r\n");
     }
   }
 
-  if (downstream_->get_blocked_request_data_eof()) {
-    return end_upload_data();
+  if (downstream_->get_blocked_request_data_eof() &&
+      downstream_->get_chunked_request()) {
+    end_upload_data_chunk();
   }
 
   return 0;
@@ -762,6 +738,17 @@ int HttpDownstreamConnection::process_blocked_request_buf() {
 
 int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
                                                      size_t datalen) {
+  if (!downstream_->get_request_header_sent()) {
+    auto output = downstream_->get_blocked_request_buf();
+    auto &req = downstream_->request();
+    output->append(data, datalen);
+    req.unconsumed_body_length += datalen;
+    if (request_header_written_) {
+      signal_write();
+    }
+    return 0;
+  }
+
   auto chunked = downstream_->get_chunked_request();
   auto output = downstream_->get_request_buf();
 
@@ -783,12 +770,26 @@ int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
 }
 
 int HttpDownstreamConnection::end_upload_data() {
+  if (!downstream_->get_request_header_sent()) {
+    downstream_->set_blocked_request_data_eof(true);
+    if (request_header_written_) {
+      signal_write();
+    }
+    return 0;
+  }
+
   signal_write();
 
   if (!downstream_->get_chunked_request()) {
     return 0;
   }
 
+  end_upload_data_chunk();
+
+  return 0;
+}
+
+void HttpDownstreamConnection::end_upload_data_chunk() {
   const auto &req = downstream_->request();
 
   auto output = downstream_->get_request_buf();
@@ -801,22 +802,10 @@ int HttpDownstreamConnection::end_upload_data() {
                                             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);
@@ -898,11 +887,12 @@ void HttpDownstreamConnection::force_resume_read() {
 }
 
 namespace {
-int htp_msg_begincb(http_parser *htp) {
+int htp_msg_begincb(llhttp_t *htp) {
   auto downstream = static_cast<Downstream *>(htp->data);
 
-  if (downstream->get_response_state() != Downstream::INITIAL) {
-    return -1;
+  if (downstream->get_response_state() != DownstreamState::INITIAL) {
+    llhttp_set_error_reason(htp, "HTTP message started when it shouldn't");
+    return HPE_USER;
   }
 
   return 0;
@@ -910,7 +900,7 @@ int htp_msg_begincb(http_parser *htp) {
 } // namespace
 
 namespace {
-int htp_hdrs_completecb(http_parser *htp) {
+int htp_hdrs_completecb(llhttp_t *htp) {
   auto downstream = static_cast<Downstream *>(htp->data);
   auto upstream = downstream->get_upstream();
   auto handler = upstream->get_client_handler();
@@ -953,20 +943,17 @@ int htp_hdrs_completecb(http_parser *htp) {
       return -1;
     }
     if (resp.fs.content_length == 0) {
-      auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH);
-      assert(cl);
-      http2::erase_header(cl);
+      resp.fs.erase_content_length_and_transfer_encoding();
     } else if (resp.fs.content_length != -1) {
       return -1;
     }
   } else if (resp.http_status / 100 == 1 ||
              (resp.http_status / 100 == 2 && req.method == HTTP_CONNECT)) {
-    if (resp.fs.header(http2::HD_CONTENT_LENGTH) ||
-        resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
-      return -1;
-    }
+    // Server MUST NOT send Content-Length and Transfer-Encoding in
+    // these responses.
+    resp.fs.erase_content_length_and_transfer_encoding();
   } else if (resp.fs.parse_content_length() != 0) {
-    downstream->set_response_state(Downstream::MSG_BAD_HEADER);
+    downstream->set_response_state(DownstreamState::MSG_BAD_HEADER);
     return -1;
   }
 
@@ -991,8 +978,8 @@ int htp_hdrs_completecb(http_parser *htp) {
     return 1;
   }
 
-  resp.connection_close = !http_should_keep_alive(htp);
-  downstream->set_response_state(Downstream::HEADER_COMPLETE);
+  resp.connection_close = !llhttp_should_keep_alive(htp);
+  downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
   downstream->inspect_http1_response();
   if (downstream->get_upgraded()) {
     // content-length must be ignored for upgraded connection.
@@ -1000,6 +987,11 @@ int htp_hdrs_completecb(http_parser *htp) {
     resp.connection_close = true;
     // transfer-encoding not applied to upgraded connection
     downstream->set_chunked_response(false);
+  } else if (http2::legacy_http1(req.http_major, req.http_minor)) {
+    if (resp.fs.content_length == -1) {
+      resp.connection_close = true;
+    }
+    downstream->set_chunked_response(false);
   } else if (!downstream->expect_response_body()) {
     downstream->set_chunked_response(false);
   }
@@ -1018,7 +1010,7 @@ int htp_hdrs_completecb(http_parser *htp) {
     if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
       return -1;
     }
-    downstream->set_request_state(Downstream::HEADER_COMPLETE);
+    downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "HTTP upgrade success. stream_id="
                 << downstream->get_stream_id();
@@ -1032,7 +1024,7 @@ int htp_hdrs_completecb(http_parser *htp) {
   // 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.
+  // llhttp.  Need test.
   return !http2::expect_response_body(req.method, resp.http_status);
 }
 } // namespace
@@ -1072,7 +1064,7 @@ int ensure_max_header_fields(const Downstream *downstream,
 } // namespace
 
 namespace {
-int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
+int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) {
   auto downstream = static_cast<Downstream *>(htp->data);
   auto &resp = downstream->response();
   auto &httpconf = get_config()->http;
@@ -1081,7 +1073,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
     return -1;
   }
 
-  if (downstream->get_response_state() == Downstream::INITIAL) {
+  if (downstream->get_response_state() == DownstreamState::INITIAL) {
     if (resp.fs.header_key_prev()) {
       resp.fs.append_last_header_key(data, len);
     } else {
@@ -1109,7 +1101,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
 } // namespace
 
 namespace {
-int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
+int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) {
   auto downstream = static_cast<Downstream *>(htp->data);
   auto &resp = downstream->response();
   auto &httpconf = get_config()->http;
@@ -1118,7 +1110,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
     return -1;
   }
 
-  if (downstream->get_response_state() == Downstream::INITIAL) {
+  if (downstream->get_response_state() == DownstreamState::INITIAL) {
     resp.fs.append_last_header_value(data, len);
   } else {
     resp.fs.append_last_trailer_value(data, len);
@@ -1128,7 +1120,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
 } // namespace
 
 namespace {
-int htp_bodycb(http_parser *htp, const char *data, size_t len) {
+int htp_bodycb(llhttp_t *htp, const char *data, size_t len) {
   auto downstream = static_cast<Downstream *>(htp->data);
   auto &resp = downstream->response();
 
@@ -1140,14 +1132,13 @@ int htp_bodycb(http_parser *htp, const char *data, size_t len) {
 } // namespace
 
 namespace {
-int htp_msg_completecb(http_parser *htp) {
+int htp_msg_completecb(llhttp_t *htp) {
   auto downstream = static_cast<Downstream *>(htp->data);
 
-  // http-parser does not treat "200 connection established" response
+  // llhttp 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.
+  // function is called, and llhttp_execute() returns just after that.
   if (downstream->get_upgraded()) {
     return 0;
   }
@@ -1158,7 +1149,7 @@ int htp_msg_completecb(http_parser *htp) {
     return 0;
   }
 
-  downstream->set_response_state(Downstream::MSG_COMPLETE);
+  downstream->set_response_state(DownstreamState::MSG_COMPLETE);
   // Block reading another response message from (broken?)
   // server. This callback is not called if the connection is
   // tunneled.
@@ -1167,22 +1158,11 @@ int htp_msg_completecb(http_parser *htp) {
 }
 } // 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 HttpDownstreamConnection::write_first() {
   int rv;
 
+  process_blocked_request_buf();
+
   if (conn_.tls.ssl) {
     rv = write_tls();
   } else {
@@ -1199,7 +1179,24 @@ int HttpDownstreamConnection::write_reuse_first() {
     on_write_ = &HttpDownstreamConnection::write_clear;
   }
 
-  reuse_first_write_done_ = true;
+  first_write_done_ = true;
+  downstream_->set_request_header_sent(true);
+
+  auto buf = downstream_->get_blocked_request_buf();
+  buf->reset();
+
+  // upstream->resume_read() might be called in
+  // write_tls()/write_clear(), but before blocked_request_buf_ is
+  // reset.  So upstream read might still be blocked.  Let's do it
+  // again here.
+  auto input = downstream_->get_request_buf();
+  if (input->rleft() == 0) {
+    auto upstream = downstream_->get_upstream();
+    auto &req = downstream_->request();
+
+    upstream->resume_read(SHRPX_NO_BUFFER, downstream_,
+                          req.unconsumed_body_length);
+  }
 
   return 0;
 }
@@ -1249,7 +1246,7 @@ int HttpDownstreamConnection::write_clear() {
     }
 
     if (nwrite < 0) {
-      if (!reuse_first_write_done_) {
+      if (!first_write_done_) {
         return nwrite;
       }
       // We may have pending data in receive buffer which may contain
@@ -1314,7 +1311,7 @@ int HttpDownstreamConnection::tls_handshake() {
   ev_set_cb(&conn_.wt, timeoutcb);
 
   on_read_ = &HttpDownstreamConnection::read_tls;
-  on_write_ = &HttpDownstreamConnection::write_tls;
+  on_write_ = &HttpDownstreamConnection::write_first;
 
   // TODO Check negotiated ALPN
 
@@ -1373,7 +1370,7 @@ int HttpDownstreamConnection::write_tls() {
     }
 
     if (nwrite < 0) {
-      if (!reuse_first_write_done_) {
+      if (!first_write_done_) {
         return nwrite;
       }
       // We may have pending data in receive buffer which may contain
@@ -1421,23 +1418,27 @@ int HttpDownstreamConnection::process_input(const uint8_t *data,
     return 0;
   }
 
+  auto htperr = llhttp_execute(&response_htp_,
+                               reinterpret_cast<const char *>(data), datalen);
   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) {
+      htperr == HPE_OK
+          ? datalen
+          : static_cast<size_t>(reinterpret_cast<const uint8_t *>(
+                                    llhttp_get_error_pos(&response_htp_)) -
+                                data);
+
+  if (htperr != HPE_OK &&
+      (!downstream_->get_upgraded() || htperr != HPE_PAUSED_UPGRADE)) {
     // Handling early return (in other words, response was hijacked by
     // mruby scripting).
-    if (downstream_->get_response_state() == Downstream::MSG_COMPLETE) {
+    if (downstream_->get_response_state() == DownstreamState::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);
+                        << "(" << llhttp_errno_name(htperr) << ") "
+                        << llhttp_get_error_reason(&response_htp_);
     }
 
     return -1;
@@ -1512,7 +1513,7 @@ int HttpDownstreamConnection::connected() {
   ev_set_cb(&conn_.wt, timeoutcb);
 
   on_read_ = &HttpDownstreamConnection::read_clear;
-  on_write_ = &HttpDownstreamConnection::write_clear;
+  on_write_ = &HttpDownstreamConnection::write_first;
 
   return 0;
 }
index 554e9b9..a453f0d 100644 (file)
@@ -27,7 +27,7 @@
 
 #include "shrpx.h"
 
-#include "http-parser/http_parser.h"
+#include "llhttp.h"
 
 #include "shrpx_downstream_connection.h"
 #include "shrpx_io_control.h"
@@ -44,7 +44,7 @@ struct DNSQuery;
 class HttpDownstreamConnection : public DownstreamConnection {
 public:
   HttpDownstreamConnection(const std::shared_ptr<DownstreamAddrGroup> &group,
-                           size_t initial_addr_idx, struct ev_loop *loop,
+                           DownstreamAddr *addr, struct ev_loop *loop,
                            Worker *worker);
   virtual ~HttpDownstreamConnection();
   virtual int attach_downstream(Downstream *downstream);
@@ -53,6 +53,7 @@ public:
   virtual int push_request_headers();
   virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
   virtual int end_upload_data();
+  void end_upload_data_chunk();
 
   virtual void pause_read(IOCtrlReason reason);
   virtual int resume_read(IOCtrlReason reason, size_t consumed);
@@ -71,7 +72,7 @@ public:
 
   int initiate_connection();
 
-  int write_reuse_first();
+  int write_first();
   int read_clear();
   int write_clear();
   int read_tls();
@@ -109,15 +110,13 @@ private:
   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_;
+  llhttp_t response_htp_;
+  // true if first write succeeded.
+  bool first_write_done_;
   // true if this object can be reused
   bool reusable_;
+  // true if request header is written to request buffer.
+  bool request_header_written_;
 };
 
 } // namespace shrpx
index b6953a7..34a5504 100644 (file)
 #include "util.h"
 #include "template.h"
 #include "base64.h"
+#include "url-parser/url_parser.h"
 
 using namespace nghttp2;
 
 namespace shrpx {
 
+namespace {
+int htp_msg_begin(llhttp_t *htp);
+int htp_uricb(llhttp_t *htp, const char *data, size_t len);
+int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len);
+int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len);
+int htp_hdrs_completecb(llhttp_t *htp);
+int htp_bodycb(llhttp_t *htp, const char *data, size_t len);
+int htp_msg_completecb(llhttp_t *htp);
+} // namespace
+
+namespace {
+constexpr llhttp_settings_t htp_hooks = {
+    htp_msg_begin,       // llhttp_cb      on_message_begin;
+    htp_uricb,           // llhttp_data_cb on_url;
+    nullptr,             // llhttp_data_cb on_status;
+    htp_hdr_keycb,       // llhttp_data_cb on_header_field;
+    htp_hdr_valcb,       // llhttp_data_cb on_header_value;
+    htp_hdrs_completecb, // llhttp_cb      on_headers_complete;
+    htp_bodycb,          // llhttp_data_cb on_body;
+    htp_msg_completecb,  // llhttp_cb      on_message_complete;
+    nullptr,             // llhttp_cb      on_chunk_header;
+    nullptr,             // llhttp_cb      on_chunk_complete;
+};
+} // namespace
+
 HttpsUpstream::HttpsUpstream(ClientHandler *handler)
     : handler_(handler),
       current_header_length_(0),
       ioctrl_(handler->get_rlimit()),
       num_requests_(0) {
-  http_parser_init(&htp_, HTTP_REQUEST);
+  llhttp_init(&htp_, HTTP_REQUEST, &htp_hooks);
   htp_.data = this;
 }
 
@@ -71,7 +97,8 @@ void HttpsUpstream::on_start_request() {
   }
   reset_current_header_length();
 
-  auto downstream = make_unique<Downstream>(this, handler_->get_mcpool(), 0);
+  auto downstream =
+      std::make_unique<Downstream>(this, handler_->get_mcpool(), 0);
 
   attach_downstream(std::move(downstream));
 
@@ -86,7 +113,7 @@ void HttpsUpstream::on_start_request() {
 }
 
 namespace {
-int htp_msg_begin(http_parser *htp) {
+int htp_msg_begin(llhttp_t *htp) {
   auto upstream = static_cast<HttpsUpstream *>(htp->data);
   upstream->on_start_request();
   return 0;
@@ -94,7 +121,7 @@ int htp_msg_begin(http_parser *htp) {
 } // namespace
 
 namespace {
-int htp_uricb(http_parser *htp, const char *data, size_t len) {
+int htp_uricb(llhttp_t *htp, const char *data, size_t len) {
   auto upstream = static_cast<HttpsUpstream *>(htp->data);
   auto downstream = upstream->get_downstream();
   auto &req = downstream->request();
@@ -110,9 +137,11 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
       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;
+    assert(downstream->get_request_state() == DownstreamState::INITIAL);
+    downstream->set_request_state(
+        DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
+    llhttp_set_error_reason(htp, "too long request URI");
+    return HPE_USER;
   }
 
   req.fs.add_extra_buffer_size(len);
@@ -129,7 +158,7 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
 } // namespace
 
 namespace {
-int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
+int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) {
   auto upstream = static_cast<HttpsUpstream *>(htp->data);
   auto downstream = upstream->get_downstream();
   auto &req = downstream->request();
@@ -140,12 +169,14 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
       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);
+    if (downstream->get_request_state() == DownstreamState::INITIAL) {
+      downstream->set_request_state(
+          DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
     }
-    return -1;
+    llhttp_set_error_reason(htp, "too large header");
+    return HPE_USER;
   }
-  if (downstream->get_request_state() == Downstream::INITIAL) {
+  if (downstream->get_request_state() == DownstreamState::INITIAL) {
     if (req.fs.header_key_prev()) {
       req.fs.append_last_header_key(data, len);
     } else {
@@ -155,8 +186,9 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
               << "Too many header field num=" << req.fs.num_fields() + 1;
         }
         downstream->set_request_state(
-            Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
-        return -1;
+            DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
+        llhttp_set_error_reason(htp, "too many headers");
+        return HPE_USER;
       }
       req.fs.alloc_add_header_name(StringRef{data, len});
     }
@@ -170,7 +202,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
           ULOG(INFO, upstream)
               << "Too many header field num=" << req.fs.num_fields() + 1;
         }
-        return -1;
+        llhttp_set_error_reason(htp, "too many headers");
+        return HPE_USER;
       }
       req.fs.alloc_add_trailer_name(StringRef{data, len});
     }
@@ -180,7 +213,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
 } // namespace
 
 namespace {
-int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
+int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) {
   auto upstream = static_cast<HttpsUpstream *>(htp->data);
   auto downstream = upstream->get_downstream();
   auto &req = downstream->request();
@@ -191,12 +224,14 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
       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);
+    if (downstream->get_request_state() == DownstreamState::INITIAL) {
+      downstream->set_request_state(
+          DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
     }
-    return -1;
+    llhttp_set_error_reason(htp, "too large header");
+    return HPE_USER;
   }
-  if (downstream->get_request_state() == Downstream::INITIAL) {
+  if (downstream->get_request_state() == DownstreamState::INITIAL) {
     req.fs.append_last_header_value(data, len);
   } else {
     req.fs.append_last_trailer_value(data, len);
@@ -282,7 +317,7 @@ void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
 } // namespace
 
 namespace {
-int htp_hdrs_completecb(http_parser *htp) {
+int htp_hdrs_completecb(llhttp_t *htp) {
   int rv;
   auto upstream = static_cast<HttpsUpstream *>(htp->data);
   if (LOG_ENABLED(INFO)) {
@@ -301,7 +336,7 @@ int htp_hdrs_completecb(http_parser *htp) {
   req.http_major = htp->http_major;
   req.http_minor = htp->http_minor;
 
-  req.connection_close = !http_should_keep_alive(htp);
+  req.connection_close = !llhttp_should_keep_alive(htp);
 
   handler->stop_read_timer();
 
@@ -314,6 +349,10 @@ int htp_hdrs_completecb(http_parser *htp) {
        << "HTTP/" << req.http_major << "." << req.http_minor << "\n";
 
     for (const auto &kv : req.fs.headers()) {
+      if (kv.name == "authorization") {
+        ss << TTY_HTTP_HD << kv.name << TTY_RST << ": <redacted>\n";
+        continue;
+      }
       ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n";
     }
 
@@ -324,15 +363,11 @@ int htp_hdrs_completecb(http_parser *htp) {
   // 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;
-    }
+    // llhttp sets 0 to htp->content_length 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.
+    req.fs.content_length = htp->content_length;
   }
 
   auto host = req.fs.header(http2::HD_HOST);
@@ -396,7 +431,7 @@ int htp_hdrs_completecb(http_parser *htp) {
     }
   }
 
-  downstream->set_request_state(Downstream::HEADER_COMPLETE);
+  downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
 
 #ifdef HAVE_MRUBY
   auto worker = handler->get_worker();
@@ -412,46 +447,48 @@ int htp_hdrs_completecb(http_parser *htp) {
 
   // mruby hook may change method value
 
-  if (req.no_authority && config->http2_proxy && !faddr->alt_mode) {
+  if (req.no_authority && config->http2_proxy &&
+      faddr->alt_mode == UpstreamAltMode::NONE) {
     // Request URI should be absolute-form for client proxy mode
     return -1;
   }
 
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
     return 0;
   }
 
-  auto dconn = handler->get_downstream_connection(rv, downstream);
+  DownstreamConnection *dconn_ptr;
 
-  if (!dconn) {
-    if (rv == SHRPX_ERR_TLS_REQUIRED) {
-      upstream->redirect_to_https(downstream);
-    }
-    downstream->set_request_state(Downstream::CONNECT_FAIL);
+  for (;;) {
+    auto dconn = handler->get_downstream_connection(rv, downstream);
 
-    return -1;
-  }
+    if (!dconn) {
+      if (rv == SHRPX_ERR_TLS_REQUIRED) {
+        upstream->redirect_to_https(downstream);
+      }
+      downstream->set_request_state(DownstreamState::CONNECT_FAIL);
+      return -1;
+    }
 
 #ifdef HAVE_MRUBY
-  auto dconn_ptr = dconn.get();
+    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;
+    if (downstream->attach_downstream_connection(std::move(dconn)) == 0) {
+      break;
+    }
   }
 
 #ifdef HAVE_MRUBY
   const auto &group = dconn_ptr->get_downstream_addr_group();
   if (group) {
-    const auto &dmruby_ctx = group->mruby_ctx;
+    const auto &dmruby_ctx = group->shared_addr->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) {
+    if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       return 0;
     }
   }
@@ -463,14 +500,12 @@ int htp_hdrs_completecb(http_parser *htp) {
     return -1;
   }
 
-  if (faddr->alt_mode) {
+  if (faddr->alt_mode != UpstreamAltMode::NONE) {
     // 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"))) {
+    if (downstream->get_expect_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);
@@ -483,7 +518,7 @@ int htp_hdrs_completecb(http_parser *htp) {
 } // namespace
 
 namespace {
-int htp_bodycb(http_parser *htp, const char *data, size_t len) {
+int htp_bodycb(llhttp_t *htp, const char *data, size_t len) {
   int rv;
   auto upstream = static_cast<HttpsUpstream *>(htp->data);
   auto downstream = upstream->get_downstream();
@@ -492,18 +527,19 @@ int htp_bodycb(http_parser *htp, const char *data, size_t 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) {
+    if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       return 0;
     }
 
-    return -1;
+    llhttp_set_error_reason(htp, "could not process request body");
+    return HPE_USER;
   }
   return 0;
 }
 } // namespace
 
 namespace {
-int htp_msg_completecb(http_parser *htp) {
+int htp_msg_completecb(llhttp_t *htp) {
   int rv;
   auto upstream = static_cast<HttpsUpstream *>(htp->data);
   if (LOG_ENABLED(INFO)) {
@@ -511,21 +547,20 @@ int htp_msg_completecb(http_parser *htp) {
   }
   auto handler = upstream->get_client_handler();
   auto downstream = upstream->get_downstream();
-  downstream->set_request_state(Downstream::MSG_COMPLETE);
+  downstream->set_request_state(DownstreamState::MSG_COMPLETE);
   rv = downstream->end_upload_data();
   if (rv != 0) {
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() == DownstreamState::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 HPE_PAUSED;
     }
-    return -1;
+    llhttp_set_error_reason(htp, "could not finish request body");
+    return HPE_USER;
   }
 
   if (handler->get_http2_upgrade_allowed() &&
@@ -537,24 +572,10 @@ int htp_msg_completecb(http_parser *htp) {
   }
 
   // Stop further processing to complete this request
-  http_parser_pause(htp, 1);
-  return 0;
+  return HPE_PAUSED;
 }
 } // 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() {
@@ -567,7 +588,7 @@ int HttpsUpstream::on_read() {
   }
 
   // downstream can be nullptr here, because it is initialized in the
-  // callback chain called by http_parser_execute()
+  // callback chain called by llhttp_execute()
   if (downstream && downstream->get_upgraded()) {
 
     auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft());
@@ -594,19 +615,23 @@ int HttpsUpstream::on_read() {
   if (downstream) {
     // To avoid reading next pipelined request
     switch (downstream->get_request_state()) {
-    case Downstream::INITIAL:
-    case Downstream::HEADER_COMPLETE:
+    case DownstreamState::INITIAL:
+    case DownstreamState::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());
+  // llhttp_execute() does nothing once it entered error state.
+  auto htperr = llhttp_execute(&htp_, reinterpret_cast<const char *>(rb->pos()),
+                               rb->rleft());
 
+  auto nread =
+      htperr == HPE_OK
+          ? rb->rleft()
+          : reinterpret_cast<const uint8_t *>(llhttp_get_error_pos(&htp_)) -
+                rb->pos();
   rb->drain(nread);
   rlimit->startw();
 
@@ -617,14 +642,12 @@ int HttpsUpstream::on_read() {
   // 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) {
+        downstream->get_request_state() == DownstreamState::MSG_COMPLETE &&
+        downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       handler_->signal_write();
     }
     return 0;
@@ -633,11 +656,12 @@ int HttpsUpstream::on_read() {
   if (htperr != HPE_OK) {
     if (LOG_ENABLED(INFO)) {
       ULOG(INFO, this) << "HTTP parse failure: "
-                       << "(" << http_errno_name(htperr) << ") "
-                       << http_errno_description(htperr);
+                       << "(" << llhttp_errno_name(htperr) << ") "
+                       << llhttp_get_error_reason(&htp_);
     }
 
-    if (downstream && downstream->get_response_state() != Downstream::INITIAL) {
+    if (downstream &&
+        downstream->get_response_state() != DownstreamState::INITIAL) {
       handler_->set_should_close_after_write(true);
       handler_->signal_write();
       return 0;
@@ -650,10 +674,10 @@ int HttpsUpstream::on_read() {
     } else if (downstream) {
       status_code = downstream->response().http_status;
       if (status_code == 0) {
-        if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
+        if (downstream->get_request_state() == DownstreamState::CONNECT_FAIL) {
           status_code = 502;
         } else if (downstream->get_request_state() ==
-                   Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) {
+                   DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE) {
           status_code = 431;
         } else {
           status_code = 400;
@@ -699,7 +723,7 @@ int HttpsUpstream::on_write() {
 
   // 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->get_response_state() == DownstreamState::MSG_COMPLETE) {
     if (downstream->can_detach_downstream_connection()) {
       // Keep-alive
       downstream->detach_downstream_connection();
@@ -709,7 +733,7 @@ int HttpsUpstream::on_write() {
       // dconn was deleted
     }
     // We need this if response ends before request.
-    if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
+    if (downstream->get_request_state() == DownstreamState::MSG_COMPLETE) {
       delete_downstream();
 
       if (handler_->get_should_close_after_write()) {
@@ -747,7 +771,7 @@ int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream,
   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);
+    llhttp_resume(&htp_);
 
     auto conn = handler_->get_connection();
     ev_feed_event(conn->loop, &conn->rev, EV_READ);
@@ -764,7 +788,10 @@ int HttpsUpstream::downstream_read(DownstreamConnection *dconn) {
   rv = downstream->on_read();
 
   if (rv == SHRPX_ERR_EOF) {
-    return downstream_eof(dconn);
+    if (downstream->get_request_header_sent()) {
+      return downstream_eof(dconn);
+    }
+    return SHRPX_ERR_RETRY;
   }
 
   if (rv == SHRPX_ERR_DCONN_CANCELED) {
@@ -776,11 +803,11 @@ int HttpsUpstream::downstream_read(DownstreamConnection *dconn) {
     return downstream_error(dconn, Downstream::EVENT_ERROR);
   }
 
-  if (downstream->get_response_state() == Downstream::MSG_RESET) {
+  if (downstream->get_response_state() == DownstreamState::MSG_RESET) {
     return -1;
   }
 
-  if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
+  if (downstream->get_response_state() == DownstreamState::MSG_BAD_HEADER) {
     error_reply(502);
     downstream->pop_downstream_connection();
     goto end;
@@ -818,23 +845,23 @@ int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) {
     DCLOG(INFO, dconn) << "EOF";
   }
 
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
     goto end;
   }
 
-  if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::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->set_response_state(DownstreamState::MSG_COMPLETE);
     downstream->pop_downstream_connection();
     goto end;
   }
 
-  if (downstream->get_response_state() == Downstream::INITIAL) {
+  if (downstream->get_response_state() == DownstreamState::INITIAL) {
     // we did not send any response headers, so we can reply error
     // message.
     if (LOG_ENABLED(INFO)) {
@@ -863,13 +890,17 @@ int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
       DCLOG(INFO, dconn) << "Timeout";
     }
   }
-  if (downstream->get_response_state() != Downstream::INITIAL) {
+  if (downstream->get_response_state() != DownstreamState::INITIAL) {
     return -1;
   }
 
   unsigned int status;
   if (events & Downstream::EVENT_TIMEOUT) {
-    status = 504;
+    if (downstream->get_request_header_sent()) {
+      status = 504;
+    } else {
+      status = 408;
+    }
   } else {
     status = 502;
   }
@@ -950,7 +981,7 @@ int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
   output->append(body, bodylen);
 
   downstream->response_sent_body_length += bodylen;
-  downstream->set_response_state(Downstream::MSG_COMPLETE);
+  downstream->set_response_state(DownstreamState::MSG_COMPLETE);
 
   return 0;
 }
@@ -959,7 +990,8 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
   auto downstream = get_downstream();
 
   if (!downstream) {
-    attach_downstream(make_unique<Downstream>(this, handler_->get_mcpool(), 1));
+    attach_downstream(
+        std::make_unique<Downstream>(this, handler_->get_mcpool(), 1));
     downstream = get_downstream();
   }
 
@@ -995,7 +1027,7 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
   output->append(html);
 
   downstream->response_sent_body_length += html.size();
-  downstream->set_response_state(Downstream::MSG_COMPLETE);
+  downstream->set_response_state(DownstreamState::MSG_COMPLETE);
 }
 
 void HttpsUpstream::attach_downstream(std::unique_ptr<Downstream> downstream) {
@@ -1042,7 +1074,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   auto &resp = downstream->response();
   auto &balloc = downstream->get_block_allocator();
   auto dconn = downstream->get_downstream_connection();
-  assert(dconn);
+  // dconn might be nullptr if this is non-final response from mruby.
 
   if (downstream->get_non_final_response() &&
       !downstream->supports_non_final_response()) {
@@ -1052,16 +1084,17 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
 
 #ifdef HAVE_MRUBY
   if (!downstream->get_non_final_response()) {
+    assert(dconn);
     const auto &group = dconn->get_downstream_addr_group();
     if (group) {
-      const auto &dmruby_ctx = group->mruby_ctx;
+      const auto &dmruby_ctx = group->shared_addr->mruby_ctx;
 
       if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
         error_reply(500);
         return -1;
       }
 
-      if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+      if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
         return -1;
       }
     }
@@ -1074,7 +1107,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
       return -1;
     }
 
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
       return -1;
     }
   }
@@ -1088,7 +1121,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   buf->append('.');
   buf->append('0' + req.http_minor);
   buf->append(' ');
-  if (req.connect_proto && downstream->get_upgraded()) {
+  if (req.connect_proto != ConnectProto::NONE && downstream->get_upgraded()) {
     buf->append(http2::stringify_status(balloc, 101));
     buf->append(' ');
     buf->append(http2::get_reason_phrase(101));
@@ -1122,8 +1155,12 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
     return 0;
   }
 
-  http2::build_http1_headers_from_headers(
-      buf, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
+  auto build_flags = (http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA) |
+                     (!http2::legacy_http1(req.http_major, req.http_minor)
+                          ? 0
+                          : http2::HDOP_STRIP_TRANSFER_ENCODING);
+
+  http2::build_http1_headers_from_headers(buf, resp.fs.headers(), build_flags);
 
   auto worker = handler_->get_worker();
 
@@ -1146,7 +1183,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   }
 
   if (!connect_method && downstream->get_upgraded()) {
-    if (req.connect_proto == CONNECT_PROTO_WEBSOCKET &&
+    if (req.connect_proto == ConnectProto::WEBSOCKET &&
         resp.http_status / 100 == 2) {
       buf->append("Upgrade: websocket\r\nConnection: Upgrade\r\n");
       auto key = req.fs.header(http2::HD_SEC_WEBSOCKET_KEY);
@@ -1305,7 +1342,7 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
 
   if (req.connection_close || resp.connection_close ||
       // To avoid to stall upload body
-      downstream->get_request_state() != Downstream::MSG_COMPLETE) {
+      downstream->get_request_state() != DownstreamState::MSG_COMPLETE) {
     auto handler = get_client_handler();
     handler->set_should_close_after_write(true);
   }
@@ -1392,14 +1429,16 @@ int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
 
   if (!downstream_->request_submission_ready()) {
     switch (downstream_->get_response_state()) {
-    case Downstream::MSG_COMPLETE:
+    case DownstreamState::MSG_COMPLETE:
       // We have got all response body already.  Send it off.
       return 0;
-    case Downstream::INITIAL:
+    case DownstreamState::INITIAL:
       if (on_downstream_abort_request(downstream_.get(), 502) != 0) {
         return -1;
       }
       return 0;
+    default:
+      break;
     }
     // Return error so that caller can delete handler
     return -1;
@@ -1413,14 +1452,16 @@ int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
     goto fail;
   }
 
-  dconn = handler_->get_downstream_connection(rv, downstream_.get());
-  if (!dconn) {
-    goto fail;
-  }
+  for (;;) {
+    auto 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_->attach_downstream_connection(std::move(dconn));
+    if (rv == 0) {
+      break;
+    }
   }
 
   rv = downstream_->push_request_headers();
index 8e83cea..d85d2de 100644 (file)
@@ -30,7 +30,7 @@
 #include <cinttypes>
 #include <memory>
 
-#include "http-parser/http_parser.h"
+#include "llhttp.h"
 
 #include "shrpx_upstream.h"
 #include "memchunk.h"
@@ -100,7 +100,7 @@ public:
 
 private:
   ClientHandler *handler_;
-  http_parser htp_;
+  llhttp_t htp_;
   size_t current_header_length_;
   std::unique_ptr<Downstream> downstream_;
   IOControl ioctrl_;
index 9d30ca7..a81e992 100644 (file)
@@ -106,7 +106,7 @@ LiveCheck::LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
             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),
+            get_config()->tls.dyn_rec.idle_timeout, Proto::NONE),
       wb_(worker->get_mcpool()),
       gen_(gen),
       read_(&LiveCheck::noop),
@@ -211,10 +211,10 @@ int LiveCheck::initiate_connection() {
     }
 
     switch (addr_->proto) {
-    case PROTO_HTTP1:
+    case Proto::HTTP1:
       tls::setup_downstream_http1_alpn(ssl);
       break;
-    case PROTO_HTTP2:
+    case Proto::HTTP2:
       tls::setup_downstream_http2_alpn(ssl);
       break;
     default:
@@ -227,11 +227,11 @@ int LiveCheck::initiate_connection() {
 
   if (addr_->dns) {
     if (!dns_query_) {
-      auto dns_query = make_unique<DNSQuery>(
-          addr_->host, [this](int status, const Address *result) {
+      auto dns_query = std::make_unique<DNSQuery>(
+          addr_->host, [this](DNSResolverStatus status, const Address *result) {
             int rv;
 
-            if (status == DNS_STATUS_OK) {
+            if (status == DNSResolverStatus::OK) {
               *this->resolved_addr_ = *result;
             }
             rv = this->initiate_connection();
@@ -242,27 +242,26 @@ int LiveCheck::initiate_connection() {
       auto dns_tracker = worker_->get_dns_tracker();
 
       if (!resolved_addr_) {
-        resolved_addr_ = make_unique<Address>();
+        resolved_addr_ = std::make_unique<Address>();
       }
 
-      rv = dns_tracker->resolve(resolved_addr_.get(), dns_query.get());
-      switch (rv) {
-      case DNS_STATUS_ERROR:
+      switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
+      case DNSResolverStatus::ERROR:
         return -1;
-      case DNS_STATUS_RUNNING:
+      case DNSResolverStatus::RUNNING:
         dns_query_ = std::move(dns_query);
         return 0;
-      case DNS_STATUS_OK:
+      case DNSResolverStatus::OK:
         break;
       default:
         assert(0);
       }
     } else {
       switch (dns_query_->status) {
-      case DNS_STATUS_ERROR:
+      case DNSResolverStatus::ERROR:
         dns_query_.reset();
         return -1;
-      case DNS_STATUS_OK:
+      case DNSResolverStatus::OK:
         dns_query_.reset();
         break;
       default:
@@ -359,7 +358,7 @@ int LiveCheck::connected() {
     return do_write();
   }
 
-  if (addr_->proto == PROTO_HTTP2) {
+  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;
@@ -418,12 +417,12 @@ int LiveCheck::tls_handshake() {
   auto proto = StringRef{next_proto, next_proto_len};
 
   switch (addr_->proto) {
-  case PROTO_HTTP1:
+  case Proto::HTTP1:
     if (proto.empty() || proto == StringRef::from_lit("http/1.1")) {
       break;
     }
     return -1;
-  case PROTO_HTTP2:
+  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.
index 8459d15..58fcb5f 100644 (file)
@@ -116,11 +116,10 @@ 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) {
+int Log::get_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 i;
     }
   }
   return -1;
@@ -228,7 +227,7 @@ Log &Log::operator<<(const ImmutableString &s) {
   return *this;
 }
 
-Log &Log::operator<<(int64_t n) {
+Log &Log::operator<<(long long n) {
   if (n >= 0) {
     return *this << static_cast<uint64_t>(n);
   }
@@ -262,7 +261,7 @@ Log &Log::operator<<(int64_t n) {
   return *this;
 }
 
-Log &Log::operator<<(uint64_t n) {
+Log &Log::operator<<(unsigned long long n) {
   if (flags_ & fmt_hex) {
     write_hex(n);
     return *this;
@@ -594,7 +593,8 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
   auto &balloc = downstream->get_block_allocator();
 
   auto downstream_addr = downstream->get_addr();
-  auto method = http2::to_method_string(req.method);
+  auto method = req.method == -1 ? StringRef::from_lit("<unknown>")
+                                 : http2::to_method_string(req.method);
   auto path = req.method == HTTP_CONNECT
                   ? req.authority
                   : config->http2_proxy
@@ -609,19 +609,19 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
 
   for (auto &lf : lfv) {
     switch (lf.type) {
-    case SHRPX_LOGF_LITERAL:
+    case LogFragmentType::LITERAL:
       std::tie(p, last) = copy(lf.value, p, last);
       break;
-    case SHRPX_LOGF_REMOTE_ADDR:
+    case LogFragmentType::REMOTE_ADDR:
       std::tie(p, last) = copy(lgsp.remote_addr, p, last);
       break;
-    case SHRPX_LOGF_TIME_LOCAL:
+    case LogFragmentType::TIME_LOCAL:
       std::tie(p, last) = copy(tstamp->time_local, p, last);
       break;
-    case SHRPX_LOGF_TIME_ISO8601:
+    case LogFragmentType::TIME_ISO8601:
       std::tie(p, last) = copy(tstamp->time_iso8601, p, last);
       break;
-    case SHRPX_LOGF_REQUEST:
+    case LogFragmentType::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);
@@ -632,13 +632,13 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
         std::tie(p, last) = copy(req.http_minor, p, last);
       }
       break;
-    case SHRPX_LOGF_STATUS:
+    case LogFragmentType::STATUS:
       std::tie(p, last) = copy(resp.http_status, p, last);
       break;
-    case SHRPX_LOGF_BODY_BYTES_SENT:
+    case LogFragmentType::BODY_BYTES_SENT:
       std::tie(p, last) = copy(downstream->response_sent_body_length, p, last);
       break;
-    case SHRPX_LOGF_HTTP: {
+    case LogFragmentType::HTTP: {
       auto hd = req.fs.header(lf.value);
       if (hd) {
         std::tie(p, last) = copy_escape((*hd).value, p, last);
@@ -649,7 +649,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
 
       break;
     }
-    case SHRPX_LOGF_AUTHORITY:
+    case LogFragmentType::AUTHORITY:
       if (!req.authority.empty()) {
         std::tie(p, last) = copy(req.authority, p, last);
         break;
@@ -658,13 +658,13 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) = copy('-', p, last);
 
       break;
-    case SHRPX_LOGF_REMOTE_PORT:
+    case LogFragmentType::REMOTE_PORT:
       std::tie(p, last) = copy(lgsp.remote_port, p, last);
       break;
-    case SHRPX_LOGF_SERVER_PORT:
+    case LogFragmentType::SERVER_PORT:
       std::tie(p, last) = copy(lgsp.server_port, p, last);
       break;
-    case SHRPX_LOGF_REQUEST_TIME: {
+    case LogFragmentType::REQUEST_TIME: {
       auto t = std::chrono::duration_cast<std::chrono::milliseconds>(
                    lgsp.request_end_time - downstream->get_request_start_time())
                    .count();
@@ -678,20 +678,20 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) = copy(frac, p, last);
       break;
     }
-    case SHRPX_LOGF_PID:
+    case LogFragmentType::PID:
       std::tie(p, last) = copy(lgsp.pid, p, last);
       break;
-    case SHRPX_LOGF_ALPN:
+    case LogFragmentType::ALPN:
       std::tie(p, last) = copy_escape(lgsp.alpn, p, last);
       break;
-    case SHRPX_LOGF_TLS_CIPHER:
+    case LogFragmentType::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:
+    case LogFragmentType::TLS_PROTOCOL:
       if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
@@ -699,7 +699,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) =
           copy(nghttp2::tls::get_tls_protocol(lgsp.ssl), p, last);
       break;
-    case SHRPX_LOGF_TLS_SESSION_ID: {
+    case LogFragmentType::TLS_SESSION_ID: {
       auto session = SSL_get_session(lgsp.ssl);
       if (!session) {
         std::tie(p, last) = copy('-', p, last);
@@ -714,7 +714,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) = copy_hex_low(session_id, session_id_length, p, last);
       break;
     }
-    case SHRPX_LOGF_TLS_SESSION_REUSED:
+    case LogFragmentType::TLS_SESSION_REUSED:
       if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
@@ -722,15 +722,15 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) =
           copy(SSL_session_reused(lgsp.ssl) ? 'r' : '.', p, last);
       break;
-    case SHRPX_LOGF_TLS_SNI:
+    case LogFragmentType::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: {
+    case LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1:
+    case LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256: {
       if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
@@ -743,8 +743,9 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       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());
+          lf.type == LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256
+              ? EVP_sha256()
+              : EVP_sha1());
       X509_free(x);
       if (len <= 0) {
         std::tie(p, last) = copy('-', p, last);
@@ -753,8 +754,8 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       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: {
+    case LogFragmentType::TLS_CLIENT_ISSUER_NAME:
+    case LogFragmentType::TLS_CLIENT_SUBJECT_NAME: {
       if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
@@ -764,7 +765,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
         std::tie(p, last) = copy('-', p, last);
         break;
       }
-      auto name = lf.type == SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME
+      auto name = lf.type == LogFragmentType::TLS_CLIENT_ISSUER_NAME
                       ? tls::get_x509_issuer_name(balloc, x)
                       : tls::get_x509_subject_name(balloc, x);
       X509_free(x);
@@ -775,7 +776,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) = copy(name, p, last);
       break;
     }
-    case SHRPX_LOGF_TLS_CLIENT_SERIAL: {
+    case LogFragmentType::TLS_CLIENT_SERIAL: {
       if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
@@ -794,21 +795,21 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) = copy(sn, p, last);
       break;
     }
-    case SHRPX_LOGF_BACKEND_HOST:
+    case LogFragmentType::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:
+    case LogFragmentType::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:
+    case LogFragmentType::NONE:
       break;
     default:
       break;
index 1130b8d..7b0b914 100644 (file)
@@ -100,12 +100,20 @@ public:
   Log &operator<<(const char *s);
   Log &operator<<(const StringRef &s);
   Log &operator<<(const ImmutableString &s);
-  Log &operator<<(int16_t n) { return *this << static_cast<int64_t>(n); }
-  Log &operator<<(int32_t n) { return *this << static_cast<int64_t>(n); }
-  Log &operator<<(int64_t n);
-  Log &operator<<(uint16_t n) { return *this << static_cast<uint64_t>(n); }
-  Log &operator<<(uint32_t n) { return *this << static_cast<uint64_t>(n); }
-  Log &operator<<(uint64_t n);
+  Log &operator<<(short n) { return *this << static_cast<long long>(n); }
+  Log &operator<<(int n) { return *this << static_cast<long long>(n); }
+  Log &operator<<(long n) { return *this << static_cast<long long>(n); }
+  Log &operator<<(long long n);
+  Log &operator<<(unsigned short n) {
+    return *this << static_cast<unsigned long long>(n);
+  }
+  Log &operator<<(unsigned int n) {
+    return *this << static_cast<unsigned long long>(n);
+  }
+  Log &operator<<(unsigned long n) {
+    return *this << static_cast<unsigned long long>(n);
+  }
+  Log &operator<<(unsigned long long n);
   Log &operator<<(float n) { return *this << static_cast<double>(n); }
   Log &operator<<(double n);
   Log &operator<<(long double n);
@@ -172,7 +180,9 @@ public:
     }
   }
   static void set_severity_level(int severity);
-  static int set_severity_level_by_name(const StringRef &name);
+  // Returns the severity level by |name|.  Returns -1 if |name| is
+  // unknown.
+  static int get_severity_level_by_name(const StringRef &name);
   static bool log_enabled(int severity) { return severity >= severity_thres_; }
 
   enum {
@@ -207,38 +217,38 @@ void dec(Log &log);
 #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,
+enum class LogFragmentType {
+  NONE,
+  LITERAL,
+  REMOTE_ADDR,
+  TIME_LOCAL,
+  TIME_ISO8601,
+  REQUEST,
+  STATUS,
+  BODY_BYTES_SENT,
+  HTTP,
+  AUTHORITY,
+  REMOTE_PORT,
+  SERVER_PORT,
+  REQUEST_TIME,
+  PID,
+  ALPN,
+  TLS_CIPHER,
+  SSL_CIPHER = TLS_CIPHER,
+  TLS_PROTOCOL,
+  SSL_PROTOCOL = TLS_PROTOCOL,
+  TLS_SESSION_ID,
+  SSL_SESSION_ID = TLS_SESSION_ID,
+  TLS_SESSION_REUSED,
+  SSL_SESSION_REUSED = TLS_SESSION_REUSED,
+  TLS_SNI,
+  TLS_CLIENT_FINGERPRINT_SHA1,
+  TLS_CLIENT_FINGERPRINT_SHA256,
+  TLS_CLIENT_ISSUER_NAME,
+  TLS_CLIENT_SERIAL,
+  TLS_CLIENT_SUBJECT_NAME,
+  BACKEND_HOST,
+  BACKEND_PORT,
 };
 
 struct LogFragment {
index 668f91a..a23184c 100644 (file)
@@ -59,7 +59,7 @@ LogConfig::LogConfig()
 #ifndef NOTHREADS
 #  ifdef HAVE_THREAD_LOCAL
 namespace {
-thread_local std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
+thread_local std::unique_ptr<LogConfig> config = std::make_unique<LogConfig>();
 } // namespace
 
 LogConfig *log_config() { return config.get(); }
@@ -88,7 +88,7 @@ void delete_log_config() { delete log_config(); }
 #  endif // !HAVE_THREAD_LOCAL
 #else    // NOTHREADS
 namespace {
-std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
+std::unique_ptr<LogConfig> config = std::make_unique<LogConfig>();
 } // namespace
 
 LogConfig *log_config() { return config.get(); }
index 99ec1eb..5f9aa67 100644 (file)
@@ -102,11 +102,12 @@ MemcachedConnection::MemcachedConnection(const Address *addr,
                                          MemchunkPool *mcpool,
                                          std::mt19937 &gen)
     : conn_(loop, -1, nullptr, mcpool, write_timeout, read_timeout, {}, {},
-            connectcb, readcb, timeoutcb, this, 0, 0., PROTO_MEMCACHED),
+            connectcb, readcb, timeoutcb, this, 0, 0., Proto::MEMCACHED),
       do_read_(&MemcachedConnection::noop),
       do_write_(&MemcachedConnection::noop),
       sni_name_(sni_name),
-      connect_blocker_(gen, loop, [] {}, [] {}),
+      connect_blocker_(
+          gen, loop, [] {}, [] {}),
       parse_state_{},
       addr_(addr),
       ssl_ctx_(ssl_ctx),
@@ -120,7 +121,8 @@ 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));
+      req->cb(req.get(),
+              MemcachedResult(MemcachedStatusCode::EXT_NETWORK_ERROR));
     }
   }
   q.clear();
@@ -424,7 +426,7 @@ int MemcachedConnection::parse_packet() {
     auto busy = false;
 
     switch (parse_state_.state) {
-    case MEMCACHED_PARSE_HEADER24: {
+    case MemcachedParseState::HEADER24: {
       if (recvbuf_.last - in < 24) {
         recvbuf_.drain_reset(in - recvbuf_.pos);
         return 0;
@@ -445,13 +447,14 @@ int MemcachedConnection::parse_packet() {
       }
       ++in;
 
-      parse_state_.op = *in++;
+      parse_state_.op = static_cast<MemcachedOp>(*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);
+      parse_state_.status_code =
+          static_cast<MemcachedStatusCode>(util::get_uint16(in));
       in += 2;
       parse_state_.totalbody = util::get_uint32(in);
       in += 4;
@@ -463,7 +466,8 @@ int MemcachedConnection::parse_packet() {
       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;
+            << static_cast<uint32_t>(req->op) << ", got "
+            << static_cast<uint32_t>(parse_state_.op);
         return -1;
       }
 
@@ -479,8 +483,9 @@ int MemcachedConnection::parse_packet() {
         return -1;
       }
 
-      if (parse_state_.op == MEMCACHED_OP_GET &&
-          parse_state_.status_code == 0 && parse_state_.extralen == 0) {
+      if (parse_state_.op == MemcachedOp::GET &&
+          parse_state_.status_code == MemcachedStatusCode::NO_ERROR &&
+          parse_state_.extralen == 0) {
         MCLOG(WARN, this) << "response for GET does not have extra";
         return -1;
       }
@@ -494,17 +499,17 @@ int MemcachedConnection::parse_packet() {
       }
 
       if (parse_state_.extralen) {
-        parse_state_.state = MEMCACHED_PARSE_EXTRA;
+        parse_state_.state = MemcachedParseState::EXTRA;
         parse_state_.read_left = parse_state_.extralen;
       } else {
-        parse_state_.state = MEMCACHED_PARSE_VALUE;
+        parse_state_.state = MemcachedParseState::VALUE;
         parse_state_.read_left = parse_state_.totalbody - parse_state_.keylen -
                                  parse_state_.extralen;
       }
       busy = true;
       break;
     }
-    case MEMCACHED_PARSE_EXTRA: {
+    case MemcachedParseState::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);
@@ -515,7 +520,7 @@ int MemcachedConnection::parse_packet() {
         recvbuf_.reset();
         return 0;
       }
-      parse_state_.state = MEMCACHED_PARSE_VALUE;
+      parse_state_.state = MemcachedParseState::VALUE;
       // since we require keylen == 0, totalbody - extralen ==
       // valuelen
       parse_state_.read_left =
@@ -523,7 +528,7 @@ int MemcachedConnection::parse_packet() {
       busy = true;
       break;
     }
-    case MEMCACHED_PARSE_VALUE: {
+    case MemcachedParseState::VALUE: {
       auto n = std::min(static_cast<size_t>(recvbuf_.last - in),
                         parse_state_.read_left);
 
@@ -537,9 +542,9 @@ int MemcachedConnection::parse_packet() {
       }
 
       if (LOG_ENABLED(INFO)) {
-        if (parse_state_.status_code) {
-          MCLOG(INFO, this)
-              << "response returned error status: " << parse_state_.status_code;
+        if (parse_state_.status_code != MemcachedStatusCode::NO_ERROR) {
+          MCLOG(INFO, this) << "response returned error status: "
+                            << static_cast<uint16_t>(parse_state_.status_code);
         }
       }
 
@@ -661,9 +666,9 @@ void MemcachedConnection::drain_send_queue(size_t nwrite) {
 
 size_t MemcachedConnection::serialized_size(MemcachedRequest *req) {
   switch (req->op) {
-  case MEMCACHED_OP_GET:
+  case MemcachedOp::GET:
     return 24 + req->key.size();
-  case MEMCACHED_OP_ADD:
+  case MemcachedOp::ADD:
   default:
     return 24 + 8 + req->key.size() + req->value.size();
   }
@@ -676,14 +681,14 @@ void MemcachedConnection::make_request(MemcachedSendbuf *sendbuf,
   std::fill(std::begin(headbuf.buf), std::end(headbuf.buf), 0);
 
   headbuf[0] = MEMCACHED_REQ_MAGIC;
-  headbuf[1] = req->op;
+  headbuf[1] = static_cast<uint8_t>(req->op);
   switch (req->op) {
-  case MEMCACHED_OP_GET:
+  case MemcachedOp::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:
+  case MemcachedOp::ADD:
     util::put_uint16be(&headbuf[2], req->key.size());
     headbuf[4] = 8;
     util::put_uint32be(&headbuf[8], 8 + req->key.size() + req->value.size());
index 516996c..2e097e1 100644 (file)
@@ -43,15 +43,17 @@ using namespace nghttp2;
 namespace shrpx {
 
 struct MemcachedRequest;
+enum class MemcachedOp : uint8_t;
+enum class MemcachedStatusCode : uint16_t;
 
-enum {
-  MEMCACHED_PARSE_HEADER24,
-  MEMCACHED_PARSE_EXTRA,
-  MEMCACHED_PARSE_VALUE,
+enum class MemcachedParseState {
+  HEADER24,
+  EXTRA,
+  VALUE,
 };
 
 // Stores state when parsing response from memcached server
-struct MemcachedParseState {
+struct MemcachedParseContext {
   // Buffer for value, dynamically allocated.
   std::vector<uint8_t> value;
   // cas in response
@@ -66,11 +68,11 @@ struct MemcachedParseState {
   // Number of bytes left to read variable length field.
   size_t read_left;
   // Parser state; see enum above
-  int state;
+  MemcachedParseState state;
   // status_code in response
-  int status_code;
+  MemcachedStatusCode status_code;
   // op in response
-  int op;
+  MemcachedOp op;
 };
 
 struct MemcachedSendbuf {
@@ -138,7 +140,7 @@ private:
   StringRef sni_name_;
   tls::TLSSessionCache tls_session_cache_;
   ConnectBlocker connect_blocker_;
-  MemcachedParseState parse_state_;
+  MemcachedParseContext parse_state_;
   const Address *addr_;
   SSL_CTX *ssl_ctx_;
   // Sum of the bytes to be transmitted in sendbufv_.
index 1b8c2c4..024bd5a 100644 (file)
@@ -37,8 +37,8 @@ MemcachedDispatcher::MemcachedDispatcher(const Address *addr,
                                          MemchunkPool *mcpool,
                                          std::mt19937 &gen)
     : loop_(loop),
-      mconn_(make_unique<MemcachedConnection>(addr, loop_, ssl_ctx, sni_name,
-                                              mcpool, gen)) {}
+      mconn_(std::make_unique<MemcachedConnection>(addr, loop_, ssl_ctx,
+                                                   sni_name, mcpool, gen)) {}
 
 MemcachedDispatcher::~MemcachedDispatcher() {}
 
index 3bd79fd..3696983 100644 (file)
@@ -35,9 +35,9 @@
 
 namespace shrpx {
 
-enum {
-  MEMCACHED_OP_GET = 0x00,
-  MEMCACHED_OP_ADD = 0x02,
+enum class MemcachedOp : uint8_t {
+  GET = 0x00,
+  ADD = 0x02,
 };
 
 struct MemcachedRequest;
@@ -50,7 +50,7 @@ struct MemcachedRequest {
   std::vector<uint8_t> value;
   MemcachedResultCallback cb;
   uint32_t expiry;
-  int op;
+  MemcachedOp op;
   bool canceled;
 };
 
index a5ac759..60a87af 100644 (file)
 
 namespace shrpx {
 
-enum MemcachedStatusCode {
-  MEMCACHED_ERR_NO_ERROR,
-  MEMCACHED_ERR_EXT_NETWORK_ERROR = 0x1001,
+enum class MemcachedStatusCode : uint16_t {
+  NO_ERROR,
+  EXT_NETWORK_ERROR = 0x1001,
 };
 
 struct MemcachedResult {
-  MemcachedResult(int status_code) : status_code(status_code) {}
-  MemcachedResult(int status_code, std::vector<uint8_t> value)
+  MemcachedResult(MemcachedStatusCode status_code) : status_code(status_code) {}
+  MemcachedResult(MemcachedStatusCode status_code, std::vector<uint8_t> value)
       : value(std::move(value)), status_code(status_code) {}
 
   std::vector<uint8_t> value;
-  int status_code;
+  MemcachedStatusCode status_code;
 };
 
 } // namespace shrpx
index ca29ef0..15466c0 100644 (file)
@@ -82,7 +82,7 @@ int MRubyContext::run_app(Downstream *downstream, int phase) {
 
   if (mrb_->exc) {
     // If response has been committed, ignore error
-    if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
+    if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) {
       rv = -1;
     }
 
@@ -180,7 +180,8 @@ RProc *compile(mrb_state *mrb, const StringRef &filename) {
 
 std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename) {
   if (filename.empty()) {
-    return make_unique<MRubyContext>(nullptr, mrb_nil_value(), mrb_nil_value());
+    return std::make_unique<MRubyContext>(nullptr, mrb_nil_value(),
+                                          mrb_nil_value());
   }
 
   auto mrb = mrb_open();
@@ -216,7 +217,7 @@ std::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename) {
   mrb_gc_protect(mrb, env);
   mrb_gc_protect(mrb, app);
 
-  return make_unique<MRubyContext>(mrb, std::move(app), std::move(env));
+  return std::make_unique<MRubyContext>(mrb, std::move(app), std::move(env));
 }
 
 mrb_sym intern_ptr(mrb_state *mrb, void *ptr) {
index 3b24ea0..1de1d5f 100644 (file)
@@ -209,7 +209,7 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
 
   auto &balloc = downstream->get_block_allocator();
 
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
     mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed");
   }
 
@@ -283,7 +283,7 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
   auto &resp = downstream->response();
   int rv;
 
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+  if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
     mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed");
   }
 
@@ -357,6 +357,10 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
     mrb_raise(mrb, E_RUNTIME_ERROR, "could not send non-final response");
   }
 
+  auto handler = upstream->get_client_handler();
+
+  handler->signal_write();
+
   return self;
 }
 } // namespace
index 82547c2..d3565db 100644 (file)
@@ -67,7 +67,7 @@ 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);
+      std::make_unique<RNode>(pat.c_str(), pat.size(), index, wildcard_index);
   add_next_node(node, std::move(new_node));
 }
 
@@ -131,8 +131,8 @@ size_t Router::add_route(const StringRef &pattern, size_t idx, bool wildcard) {
     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);
+      auto new_node = std::make_unique<RNode>(
+          &node->s[j], node->len - j, node->index, node->wildcard_index);
       std::swap(node->next, new_node->next);
 
       node->len = j;
@@ -220,9 +220,16 @@ const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
           return node;
         }
 
+        // The last '/' handling, see below.
+        node = find_next_node(node, '/');
+        if (node != nullptr && node->index != -1 && node->len == 1) {
+          return node;
+        }
+
         return nullptr;
       }
 
+      // The last '/' handling, see below.
       if (node->index != -1 && offset + n + 1 == node->len &&
           node->s[node->len - 1] == '/') {
         return node;
@@ -265,6 +272,13 @@ const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
           return node;
         }
 
+        // The last '/' handling, see below.
+        node = find_next_node(node, '/');
+        if (node != nullptr && node->index != -1 && node->len == 1) {
+          *pattern_is_wildcard = false;
+          return node;
+        }
+
         return found_node;
       }
 
index 9a93e71..21c2f51 100644 (file)
@@ -45,6 +45,9 @@ void test_shrpx_router_match(void) {
       {StringRef::from_lit("www.nghttp2.org/alpha/"), 4},
       {StringRef::from_lit("/alpha"), 5},
       {StringRef::from_lit("example.com/alpha/"), 6},
+      {StringRef::from_lit("nghttp2.org/alpha/bravo2/"), 7},
+      {StringRef::from_lit("www2.nghttp2.org/alpha/"), 8},
+      {StringRef::from_lit("www2.nghttp2.org/alpha2/"), 9},
   };
 
   Router router;
@@ -84,6 +87,13 @@ void test_shrpx_router_match(void) {
   idx = router.match(StringRef::from_lit("nghttp2.org"),
                      StringRef::from_lit("/alpha/bravo"));
 
+  CU_ASSERT(3 == idx);
+
+  idx = router.match(StringRef::from_lit("www2.nghttp2.org"),
+                     StringRef::from_lit("/alpha"));
+
+  CU_ASSERT(8 == idx);
+
   idx = router.match(StringRef{}, StringRef::from_lit("/alpha"));
 
   CU_ASSERT(5 == idx);
index 5e16bf3..5070d16 100644 (file)
@@ -110,13 +110,13 @@ int signal_set_handler(void (*handler)(int), Signals &&sigs) {
 } // namespace
 
 namespace {
-constexpr auto master_proc_ign_signals = std::array<int, 1>{{SIGPIPE}};
+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}};
+    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() {
index 76a6e76..746311f 100644 (file)
@@ -329,8 +329,8 @@ int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
     LOG(INFO) << "Memcached: cache session, id=" << util::format_hex(id, idlen);
   }
 
-  auto req = make_unique<MemcachedRequest>();
-  req->op = MEMCACHED_OP_ADD;
+  auto req = std::make_unique<MemcachedRequest>();
+  req->op = MemcachedOp::ADD;
   req->key = MEMCACHED_SESSION_CACHE_KEY_PREFIX.str();
   req->key +=
       util::format_hex(balloc, StringRef{id, static_cast<size_t>(idlen)});
@@ -343,12 +343,14 @@ int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
   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="
+                << ", status_code=" << static_cast<uint16_t>(res.status_code)
+                << ", value="
                 << std::string(std::begin(res.value), std::end(res.value));
     }
-    if (res.status_code != 0) {
+    if (res.status_code != MemcachedStatusCode::NO_ERROR) {
       LOG(WARN) << "Memcached: failed to cache session key=" << req->key
-                << ", status_code=" << res.status_code << ", value="
+                << ", status_code=" << static_cast<uint16_t>(res.status_code)
+                << ", value="
                 << std::string(std::begin(res.value), std::end(res.value));
     }
   };
@@ -362,11 +364,11 @@ int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
 
 namespace {
 SSL_SESSION *tls_session_get_cb(SSL *ssl,
-#if OPENSSL_1_1_API
+#if OPENSSL_1_1_API || LIBRESSL_2_7_API
                                 const unsigned char *id,
-#else  // !OPENSSL_1_1_API
+#else  // !(OPENSSL_1_1_API || LIBRESSL_2_7_API)
                                 unsigned char *id,
-#endif // !OPENSSL_1_1_API
+#endif // !(OPENSSL_1_1_API || LIBRESSL_2_7_API)
                                 int idlen, int *copy) {
   auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
   auto handler = static_cast<ClientHandler *>(conn->data);
@@ -397,14 +399,15 @@ SSL_SESSION *tls_session_get_cb(SSL *ssl,
               << util::format_hex(id, idlen);
   }
 
-  auto req = make_unique<MemcachedRequest>();
-  req->op = MEMCACHED_OP_GET;
+  auto req = std::make_unique<MemcachedRequest>();
+  req->op = MemcachedOp::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;
+      LOG(INFO) << "Memcached: returned status code "
+                << static_cast<uint16_t>(res.status_code);
     }
 
     // We might stop reading, so start it again
@@ -415,8 +418,8 @@ SSL_SESSION *tls_session_get_cb(SSL *ssl,
     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;
+    if (res.status_code != MemcachedStatusCode::NO_ERROR) {
+      conn->tls.handshake_state = TLSHandshakeState::CANCEL_SESSION_CACHE;
       return;
     }
 
@@ -427,15 +430,15 @@ SSL_SESSION *tls_session_get_cb(SSL *ssl,
       if (LOG_ENABLED(INFO)) {
         LOG(INFO) << "cannot materialize session";
       }
-      conn->tls.handshake_state = TLS_CONN_CANCEL_SESSION_CACHE;
+      conn->tls.handshake_state = TLSHandshakeState::CANCEL_SESSION_CACHE;
       return;
     }
 
     conn->tls.cached_session = session;
-    conn->tls.handshake_state = TLS_CONN_GOT_SESSION_CACHE;
+    conn->tls.handshake_state = TLSHandshakeState::GOT_SESSION_CACHE;
   };
 
-  conn->tls.handshake_state = TLS_CONN_WAIT_FOR_SESSION_CACHE;
+  conn->tls.handshake_state = TLSHandshakeState::WAIT_FOR_SESSION_CACHE;
   conn->tls.cached_session_lookup_req = req.get();
 
   dispatcher->add_request(std::move(req));
@@ -958,7 +961,8 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
   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
+#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L &&               \
+    !defined(OPENSSL_IS_BORINGSSL)
   // SSL_extension_supported(TLSEXT_TYPE_signed_certificate_timestamp)
   // returns 1, which means OpenSSL internally handles it.  But
   // OpenSSL handles signed_certificate_timestamp extension specially,
@@ -989,7 +993,8 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
     }
 #  endif // !OPENSSL_1_1_1_API
   }
-#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L &&
+       // !defined(OPENSSL_IS_BORINGSSL)
 
 #if OPENSSL_1_1_1_API
   if (SSL_CTX_set_max_early_data(ssl_ctx, tlsconf.max_early_data) != 1) {
@@ -1049,9 +1054,9 @@ int select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
                          void *arg) {
   auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
   switch (conn->proto) {
-  case PROTO_HTTP1:
+  case Proto::HTTP1:
     return select_h1_next_proto_cb(ssl, out, outlen, in, inlen, arg);
-  case PROTO_HTTP2:
+  case Proto::HTTP2:
     return select_h2_next_proto_cb(ssl, out, outlen, in, inlen, arg);
   default:
     return SSL_TLSEXT_ERR_NOACK;
@@ -1831,7 +1836,7 @@ std::unique_ptr<CertLookupTree> create_cert_lookup_tree() {
   if (!upstream_tls_enabled(config->conn)) {
     return nullptr;
   }
-  return make_unique<CertLookupTree>();
+  return std::make_unique<CertLookupTree>();
 }
 
 namespace {
@@ -2025,7 +2030,7 @@ StringRef get_x509_issuer_name(BlockAllocator &balloc, X509 *x) {
 #endif /* !WORDS_BIGENDIAN */
 
 StringRef get_x509_serial(BlockAllocator &balloc, X509 *x) {
-#if OPENSSL_1_1_API
+#if OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
   auto sn = X509_get0_serialNumber(x);
   uint64_t r;
   if (ASN1_INTEGER_get_uint64(&r, sn) != 1) {
@@ -2035,17 +2040,17 @@ StringRef get_x509_serial(BlockAllocator &balloc, X509 *x) {
   r = bswap64(r);
   return util::format_hex(
       balloc, StringRef{reinterpret_cast<uint8_t *>(&r), sizeof(r)});
-#else  // !OPENSSL_1_1_API
+#else  // !OPENSSL_1_1_API || OPENSSL_IS_BORINGSSL
   auto sn = X509_get_serialNumber(x);
   auto bn = BN_new();
   auto bn_d = defer(BN_free, bn);
-  if (!ASN1_INTEGER_to_BN(sn, bn)) {
+  if (!ASN1_INTEGER_to_BN(sn, bn) || BN_num_bytes(bn) > 20) {
     return StringRef{};
   }
 
-  std::array<uint8_t, 8> b;
+  std::array<uint8_t, 20> b;
   auto n = BN_bn2bin(bn, b.data());
-  assert(n == b.size());
+  assert(n <= 20);
 
   return util::format_hex(balloc, StringRef{std::begin(b), std::end(b)});
 #endif // !OPENSSL_1_1_API
@@ -2065,7 +2070,7 @@ int time_t_from_asn1_time(time_t &t, const ASN1_TIME *at) {
   }
 
   t = nghttp2_timegm(&tm);
-#else  // !OPENSSL_1_1_1_API
+#else // !OPENSSL_1_1_1_API
   auto b = BIO_new(BIO_s_mem());
   if (!b) {
     return -1;
@@ -2078,7 +2083,11 @@ int time_t_from_asn1_time(time_t &t, const ASN1_TIME *at) {
     return -1;
   }
 
+#  if defined(OPENSSL_IS_BORINGSSL)
+  char *s;
+#  else
   unsigned char *s;
+#  endif
   auto slen = BIO_get_mem_data(b, &s);
   auto tt = util::parse_openssl_asn1_time_print(
       StringRef{s, static_cast<size_t>(slen)});
index b227dda..0f130f6 100644 (file)
@@ -36,7 +36,7 @@ using namespace nghttp2;
 namespace shrpx {
 
 void test_shrpx_tls_create_lookup_tree(void) {
-  auto tree = make_unique<tls::CertLookupTree>();
+  auto tree = std::make_unique<tls::CertLookupTree>();
 
   constexpr StringRef hostnames[] = {
       StringRef::from_lit("example.com"),             // 0
@@ -85,7 +85,7 @@ void test_shrpx_tls_create_lookup_tree(void) {
   };
   num = array_size(names);
 
-  tree = make_unique<tls::CertLookupTree>();
+  tree = std::make_unique<tls::CertLookupTree>();
   for (size_t idx = 0; idx < num; ++idx) {
     tree->add_cert(names[idx], idx);
   }
@@ -123,7 +123,7 @@ void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void) {
       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>();
+  auto nghttp2_tls_ctx_data = std::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);
@@ -134,7 +134,7 @@ void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void) {
       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>();
+  auto examples_tls_ctx_data = std::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);
index 05caa19..cc0254d 100644 (file)
@@ -74,14 +74,17 @@ 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, int64_t, int64_t>;
+using DownstreamKey =
+    std::tuple<std::vector<std::tuple<StringRef, StringRef, StringRef, size_t,
+                                      size_t, Proto, uint32_t, uint32_t,
+                                      uint32_t, bool, bool, bool, bool>>,
+               bool, SessionAffinity, StringRef, StringRef,
+               SessionAffinityCookieSecure, int64_t, int64_t, StringRef>;
 
 namespace {
-DownstreamKey create_downstream_key(
-    const std::shared_ptr<SharedDownstreamAddr> &shared_addr) {
+DownstreamKey
+create_downstream_key(const std::shared_ptr<SharedDownstreamAddr> &shared_addr,
+                      const StringRef &mruby_file) {
   DownstreamKey dkey;
 
   auto &addrs = std::get<0>(dkey);
@@ -90,14 +93,17 @@ DownstreamKey create_downstream_key(
   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;
+    std::get<2>(*p) = a.group;
+    std::get<3>(*p) = a.fall;
+    std::get<4>(*p) = a.rise;
+    std::get<5>(*p) = a.proto;
+    std::get<6>(*p) = a.port;
+    std::get<7>(*p) = a.weight;
+    std::get<8>(*p) = a.group_weight;
+    std::get<9>(*p) = a.host_unix;
+    std::get<10>(*p) = a.tls;
+    std::get<11>(*p) = a.dns;
+    std::get<12>(*p) = a.upgrade_scheme;
     ++p;
   }
   std::sort(std::begin(addrs), std::end(addrs));
@@ -112,6 +118,7 @@ DownstreamKey create_downstream_key(
   auto &timeout = shared_addr->timeout;
   std::get<6>(dkey) = timeout.read;
   std::get<7>(dkey) = timeout.write;
+  std::get<8>(dkey) = mruby_file;
 
   return dkey;
 }
@@ -133,7 +140,7 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
       conn_handler_(conn_handler),
       ticket_keys_(ticket_keys),
       connect_blocker_(
-          make_unique<ConnectBlocker>(randgen_, loop_, []() {}, []() {})),
+          std::make_unique<ConnectBlocker>(randgen_, loop_, nullptr, nullptr)),
       graceful_shutdown_(false) {
   ev_async_init(&w_, eventcb);
   w_.data = this;
@@ -148,7 +155,7 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
   auto &session_cacheconf = get_config()->tls.session_cache;
 
   if (!session_cacheconf.memcached.host.empty()) {
-    session_cache_memcached_dispatcher_ = make_unique<MemcachedDispatcher>(
+    session_cache_memcached_dispatcher_ = std::make_unique<MemcachedDispatcher>(
         &session_cacheconf.memcached.addr, loop,
         tls_session_cache_memcached_ssl_ctx,
         StringRef{session_cacheconf.memcached.host}, &mcpool_, randgen_);
@@ -157,18 +164,46 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
   replace_downstream_config(std::move(downstreamconf));
 }
 
+namespace {
+void ensure_enqueue_addr(
+    std::priority_queue<WeightGroupEntry, std::vector<WeightGroupEntry>,
+                        WeightGroupEntryGreater> &wgpq,
+    WeightGroup *wg, DownstreamAddr *addr) {
+  uint32_t cycle;
+  if (!wg->pq.empty()) {
+    auto &top = wg->pq.top();
+    cycle = top.cycle;
+  } else {
+    cycle = 0;
+  }
+
+  addr->cycle = cycle;
+  addr->pending_penalty = 0;
+  wg->pq.push(DownstreamAddrEntry{addr, addr->seq, addr->cycle});
+  addr->queued = true;
+
+  if (!wg->queued) {
+    if (!wgpq.empty()) {
+      auto &top = wgpq.top();
+      cycle = top.cycle;
+    } else {
+      cycle = 0;
+    }
+
+    wg->cycle = cycle;
+    wg->pending_penalty = 0;
+    wgpq.push(WeightGroupEntry{wg, wg->seq, wg->cycle});
+    wg->queued = true;
+  }
+}
+} // namespace
+
 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();
     }
@@ -198,22 +233,12 @@ void Worker::replace_downstream_config(
     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) {
+    if (src.affinity.type == SessionAffinity::COOKIE) {
       shared_addr->affinity.cookie.name =
           make_string_ref(shared_addr->balloc, src.affinity.cookie.name);
       if (!src.affinity.cookie.path.empty()) {
@@ -227,9 +252,6 @@ void Worker::replace_downstream_config(
     shared_addr->timeout.read = src.timeout.read;
     shared_addr->timeout.write = src.timeout.write;
 
-    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];
@@ -240,6 +262,9 @@ void Worker::replace_downstream_config(
           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.weight = src_addr.weight;
+      dst_addr.group = make_string_ref(shared_addr->balloc, src_addr.group);
+      dst_addr.group_weight = src_addr.group_weight;
       dst_addr.proto = src_addr.proto;
       dst_addr.tls = src_addr.tls;
       dst_addr.sni = make_string_ref(shared_addr->balloc, src_addr.sni);
@@ -250,62 +275,78 @@ void Worker::replace_downstream_config(
 
       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;
-      }
+      dst_addr.connect_blocker = std::make_unique<ConnectBlocker>(
+          randgen_, loop_, nullptr, [shared_addr_ptr, &dst_addr]() {
+            if (!dst_addr.queued) {
+              if (!dst_addr.wg) {
+                return;
+              }
+              ensure_enqueue_addr(shared_addr_ptr->pq, dst_addr.wg, &dst_addr);
+            }
+          });
+
+      dst_addr.live_check = std::make_unique<LiveCheck>(
+          loop_, cl_ssl_ctx_, this, &dst_addr, randgen_);
     }
 
+#ifdef HAVE_MRUBY
+    auto mruby_ctx_it = shared_mruby_ctxs.find(src.mruby_file);
+    if (mruby_ctx_it == std::end(shared_mruby_ctxs)) {
+      shared_addr->mruby_ctx = mruby::create_mruby_context(src.mruby_file);
+      assert(shared_addr->mruby_ctx);
+      shared_mruby_ctxs.emplace(src.mruby_file, shared_addr->mruby_ctx);
+    } else {
+      shared_addr->mruby_ctx = (*mruby_ctx_it).second;
+    }
+#endif // HAVE_MRUBY
+
     // share the connection if patterns have the same set of backend
     // addresses.
 
-    auto dkey = create_downstream_key(shared_addr);
+    auto dkey = create_downstream_key(shared_addr, src.mruby_file);
     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;
+      std::shuffle(std::begin(shared_addr->addrs), std::end(shared_addr->addrs),
+                   randgen_);
+
+      size_t seq = 0;
+      for (auto &addr : shared_addr->addrs) {
+        addr.dconn_pool = std::make_unique<DownstreamConnectionPool>();
+        addr.seq = seq++;
       }
 
-      shared_addr->http1_pri.weight = num_http1;
-      shared_addr->http2_pri.weight = num_http2;
+      if (shared_addr->affinity.type == SessionAffinity::NONE) {
+        std::map<StringRef, WeightGroup *> wgs;
+        size_t num_wgs = 0;
+        for (auto &addr : shared_addr->addrs) {
+          if (wgs.find(addr.group) == std::end(wgs)) {
+            ++num_wgs;
+            wgs.emplace(addr.group, nullptr);
+          }
+        }
+
+        shared_addr->wgs = std::vector<WeightGroup>(num_wgs);
 
-      if (shared_addr->affinity.type != AFFINITY_NONE) {
         for (auto &addr : shared_addr->addrs) {
-          addr.dconn_pool = make_unique<DownstreamConnectionPool>();
+          auto &wg = wgs[addr.group];
+          if (wg == nullptr) {
+            wg = &shared_addr->wgs[--num_wgs];
+            wg->seq = num_wgs;
+          }
+
+          wg->weight = addr.group_weight;
+          wg->pq.push(DownstreamAddrEntry{&addr, addr.seq, addr.cycle});
+          addr.queued = true;
+          addr.wg = wg;
+        }
+
+        assert(num_wgs == 0);
+
+        for (auto &kv : wgs) {
+          shared_addr->pq.push(
+              WeightGroupEntry{kv.second, kv.second->seq, kv.second->cycle});
+          kv.second->queued = true;
         }
       }
 
@@ -368,9 +409,9 @@ void Worker::process_events() {
     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.
+    // WorkerEventType::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_);
@@ -388,7 +429,7 @@ void Worker::process_events() {
   auto worker_connections = config->conn.upstream.worker_connections;
 
   switch (wev.type) {
-  case NEW_CONNECTION: {
+  case WorkerEventType::NEW_CONNECTION: {
     if (LOG_ENABLED(INFO)) {
       WLOG(INFO, this) << "WorkerEvent: client_fd=" << wev.client_fd
                        << ", addrlen=" << wev.client_addrlen;
@@ -422,14 +463,14 @@ void Worker::process_events() {
 
     break;
   }
-  case REOPEN_LOG:
+  case WorkerEventType::REOPEN_LOG:
     WLOG(NOTICE, this) << "Reopening log files: worker process (thread " << this
                        << ")";
 
     reopen_log_files(config->logging);
 
     break;
-  case GRACEFUL_SHUTDOWN:
+  case WorkerEventType::GRACEFUL_SHUTDOWN:
     WLOG(NOTICE, this) << "Graceful shutdown commencing";
 
     graceful_shutdown_ = true;
@@ -441,7 +482,7 @@ void Worker::process_events() {
     }
 
     break;
-  case REPLACE_DOWNSTREAM:
+  case WorkerEventType::REPLACE_DOWNSTREAM:
     WLOG(NOTICE, this) << "Replace downstream";
 
     replace_downstream_config(wev.downstreamconf);
@@ -449,7 +490,7 @@ void Worker::process_events() {
     break;
   default:
     if (LOG_ENABLED(INFO)) {
-      WLOG(INFO, this) << "unknown event type " << wev.type;
+      WLOG(INFO, this) << "unknown event type " << static_cast<int>(wev.type);
     }
   }
 }
index cc8c2a0..53f37cb 100644 (file)
@@ -33,6 +33,7 @@
 #include <unordered_map>
 #include <deque>
 #include <thread>
+#include <queue>
 #ifndef NOTHREADS
 #  include <future>
 #endif // NOTHREADS
@@ -73,6 +74,8 @@ namespace tls {
 class CertLookupTree;
 } // namespace tls
 
+struct WeightGroup;
+
 struct DownstreamAddr {
   Address addr;
   // backend address.  If |host_unix| is true, this is UNIX domain
@@ -96,21 +99,33 @@ struct DownstreamAddr {
   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.
+  // 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.
   DList<Http2Session> http2_extra_freelist;
-  // true if Http2Session for this address is in group scope
-  // SharedDownstreamAddr.http2_avail_freelist
-  bool in_avail;
+  WeightGroup *wg;
   // total number of streams created in HTTP/2 connections for this
   // address.
   size_t num_dconn;
+  // the sequence number of this address to randomize the order access
+  // threads.
+  size_t seq;
   // Application protocol used in this backend
-  shrpx_proto proto;
+  Proto proto;
+  // cycle is used to prioritize this address.  Lower value takes
+  // higher priority.
+  uint32_t cycle;
+  // penalty which is applied to the next cycle calculation.
+  uint32_t pending_penalty;
+  // Weight of this address inside a weight group.  Its range is [1,
+  // 256], inclusive.
+  uint32_t weight;
+  // name of group which this address belongs to.
+  StringRef group;
+  // Weight of the weight group which this address belongs to.  Its
+  // range is [1, 256], inclusive.
+  uint32_t group_weight;
   // true if TLS is used in this backend
   bool tls;
   // true if dynamic DNS is enabled
@@ -119,29 +134,64 @@ struct DownstreamAddr {
   // variant (e.g., "https") when forwarding request to a backend
   // connected by TLS connection.
   bool upgrade_scheme;
+  // true if this address is queued.
+  bool queued;
 };
 
-// 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.
+constexpr uint32_t MAX_DOWNSTREAM_ADDR_WEIGHT = 256;
+
+struct DownstreamAddrEntry {
+  DownstreamAddr *addr;
+  size_t seq;
   uint32_t cycle;
-  // weight, larger weight means more frequent use.
+};
+
+struct DownstreamAddrEntryGreater {
+  bool operator()(const DownstreamAddrEntry &lhs,
+                  const DownstreamAddrEntry &rhs) const {
+    auto d = lhs.cycle - rhs.cycle;
+    if (d == 0) {
+      return rhs.seq < lhs.seq;
+    }
+    return d <= MAX_DOWNSTREAM_ADDR_WEIGHT;
+  }
+};
+
+struct WeightGroup {
+  std::priority_queue<DownstreamAddrEntry, std::vector<DownstreamAddrEntry>,
+                      DownstreamAddrEntryGreater>
+      pq;
+  size_t seq;
   uint32_t weight;
+  uint32_t cycle;
+  uint32_t pending_penalty;
+  // true if this object is queued.
+  bool queued;
+};
+
+struct WeightGroupEntry {
+  WeightGroup *wg;
+  size_t seq;
+  uint32_t cycle;
+};
+
+struct WeightGroupEntryGreater {
+  bool operator()(const WeightGroupEntry &lhs,
+                  const WeightGroupEntry &rhs) const {
+    auto d = lhs.cycle - rhs.cycle;
+    if (d == 0) {
+      return rhs.seq < lhs.seq;
+    }
+    return d <= MAX_DOWNSTREAM_ADDR_WEIGHT;
+  }
 };
 
 struct SharedDownstreamAddr {
   SharedDownstreamAddr()
       : balloc(1024, 1024),
-        affinity{AFFINITY_NONE},
-        next{0},
-        http1_pri{},
-        http2_pri{},
-        redirect_if_not_tls{false} {}
+        affinity{SessionAffinity::NONE},
+        redirect_if_not_tls{false},
+        timeout{} {}
 
   SharedDownstreamAddr(const SharedDownstreamAddr &) = delete;
   SharedDownstreamAddr(SharedDownstreamAddr &&) = delete;
@@ -150,32 +200,18 @@ struct SharedDownstreamAddr {
 
   BlockAllocator balloc;
   std::vector<DownstreamAddr> addrs;
+  std::vector<WeightGroup> wgs;
+  std::priority_queue<WeightGroupEntry, std::vector<WeightGroupEntry>,
+                      WeightGroupEntryGreater>
+      pq;
   // Bunch of session affinity hash.  Only used if affinity ==
-  // AFFINITY_IP.
+  // SessionAffinity::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;
+#ifdef HAVE_MRUBY
+  std::shared_ptr<mruby::MRubyContext> mruby_ctx;
+#endif // HAVE_MRUBY
   // 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.
@@ -198,9 +234,6 @@ struct DownstreamAddrGroup {
 
   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.
@@ -211,7 +244,7 @@ struct WorkerStat {
   size_t num_connections;
 };
 
-enum WorkerEventType {
+enum class WorkerEventType {
   NEW_CONNECTION = 0x01,
   REOPEN_LOG = 0x02,
   GRACEFUL_SHUTDOWN = 0x03,
index 8c92ce0..dc95701 100644 (file)
@@ -270,14 +270,14 @@ void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w,
   auto conn_handler = static_cast<ConnectionHandler *>(w->data);
   auto dispatcher = conn_handler->get_tls_ticket_key_memcached_dispatcher();
 
-  auto req = make_unique<MemcachedRequest>();
+  auto req = std::make_unique<MemcachedRequest>();
   req->key = "nghttpx:tls-ticket-key";
-  req->op = MEMCACHED_OP_GET;
+  req->op = MemcachedOp::GET;
   req->cb = [conn_handler, w](MemcachedRequest *req, MemcachedResult res) {
     switch (res.status_code) {
-    case MEMCACHED_ERR_NO_ERROR:
+    case MemcachedStatusCode::NO_ERROR:
       break;
-    case MEMCACHED_ERR_EXT_NETWORK_ERROR:
+    case MemcachedStatusCode::EXT_NETWORK_ERROR:
       conn_handler->on_tls_ticket_key_network_error(w);
       return;
     default:
@@ -411,16 +411,16 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
 
   auto gen = util::make_mt19937();
 
-  auto conn_handler = make_unique<ConnectionHandler>(loop, gen);
+  auto conn_handler = std::make_unique<ConnectionHandler>(loop, gen);
 
   for (auto &addr : config->conn.listener.addrs) {
     conn_handler->add_acceptor(
-        make_unique<AcceptHandler>(&addr, conn_handler.get()));
+        std::make_unique<AcceptHandler>(&addr, conn_handler.get()));
   }
 
 #ifdef HAVE_NEVERBLEED
   std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
-  auto nb = make_unique<neverbleed_t>();
+  auto nb = std::make_unique<neverbleed_t>();
   if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
     LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
     return -1;
@@ -452,7 +452,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
       }
 
       conn_handler->set_tls_ticket_key_memcached_dispatcher(
-          make_unique<MemcachedDispatcher>(
+          std::make_unique<MemcachedDispatcher>(
               &ticketconf.memcached.addr, loop, ssl_ctx,
               StringRef{memcachedconf.host}, &mcpool, gen));
 
index 90cb93a..b2f850d 100644 (file)
 #include <typeinfo>
 #include <algorithm>
 #include <ostream>
+#include <utility>
 
 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<
@@ -105,21 +90,17 @@ template <typename T> struct DList {
   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;
-  }
+      : head{std::exchange(other.head, nullptr)},
+        tail{std::exchange(other.tail, nullptr)},
+        len{std::exchange(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;
+    head = std::exchange(other.head, nullptr);
+    tail = std::exchange(other.tail, nullptr);
+    len = std::exchange(other.len, 0);
 
     return *this;
   }
@@ -201,7 +182,7 @@ 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);
+  auto res = std::make_unique<char[]>(last - first + 1);
   *std::copy(first, last, res.get()) = '\0';
   return res;
 }
@@ -266,10 +247,7 @@ public:
   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 = "";
-  }
+      : len{std::exchange(other.len, 0)}, base{std::exchange(other.base, "")} {}
   ~ImmutableString() {
     if (len) {
       delete[] base;
@@ -294,10 +272,8 @@ public:
     if (len) {
       delete[] base;
     }
-    len = other.len;
-    base = other.base;
-    other.len = 0;
-    other.base = "";
+    len = std::exchange(other.len, 0);
+    base = std::exchange(other.base, "");
     return *this;
   }
 
@@ -554,4 +530,19 @@ inline int run_app(std::function<int(int, char **)> app, int argc,
 
 } // namespace nghttp2
 
+namespace std {
+template <> struct hash<nghttp2::StringRef> {
+  std::size_t operator()(const nghttp2::StringRef &s) const noexcept {
+    // 32 bit FNV-1a:
+    // https://tools.ietf.org/html/draft-eastlake-fnv-16#section-6.1.1
+    uint32_t h = 2166136261u;
+    for (auto c : s) {
+      h ^= static_cast<uint8_t>(c);
+      h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
+    }
+    return h;
+  }
+};
+} // namespace std
+
 #endif // TEMPLATE_H
index b3c8de5..bb30fcd 100644 (file)
@@ -1542,6 +1542,46 @@ std::mt19937 make_mt19937() {
   return std::mt19937(rd());
 }
 
+int daemonize(int nochdir, int noclose) {
+#if defined(__APPLE__)
+  pid_t pid;
+  pid = fork();
+  if (pid == -1) {
+    return -1;
+  } else if (pid > 0) {
+    _exit(EXIT_SUCCESS);
+  }
+  if (setsid() == -1) {
+    return -1;
+  }
+  pid = fork();
+  if (pid == -1) {
+    return -1;
+  } else if (pid > 0) {
+    _exit(EXIT_SUCCESS);
+  }
+  if (nochdir == 0) {
+    if (chdir("/") == -1) {
+      return -1;
+    }
+  }
+  if (noclose == 0) {
+    if (freopen("/dev/null", "r", stdin) == nullptr) {
+      return -1;
+    }
+    if (freopen("/dev/null", "w", stdout) == nullptr) {
+      return -1;
+    }
+    if (freopen("/dev/null", "w", stderr) == nullptr) {
+      return -1;
+    }
+  }
+  return 0;
+#else  // !defined(__APPLE__)
+  return daemon(nochdir, noclose);
+#endif // !defined(__APPLE__)
+}
+
 } // namespace util
 
 } // namespace nghttp2
index 897f4ef..38761e0 100644 (file)
@@ -47,7 +47,7 @@
 #include <map>
 #include <random>
 
-#include "http-parser/http_parser.h"
+#include "url-parser/url_parser.h"
 
 #include "template.h"
 #include "network.h"
@@ -772,6 +772,10 @@ StringRef extract_host(const StringRef &hostport);
 // Returns new std::mt19937 object.
 std::mt19937 make_mt19937();
 
+// daemonize calls daemon(3).  If __APPLE__ is defined, it implements
+// daemon() using fork().
+int daemonize(int nochdir, int noclose);
+
 } // namespace util
 
 } // namespace nghttp2
index dcf0848..7d9f97f 100644 (file)
@@ -115,7 +115,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -543,7 +543,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index 13865de..46e9b1c 100644 (file)
@@ -91,6 +91,8 @@ int main() {
                    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_recv_headers_for_closed_stream",
+                   test_nghttp2_session_recv_headers_for_closed_stream) ||
       !CU_add_test(pSuite, "session_server_recv_push_response",
                    test_nghttp2_session_server_recv_push_response) ||
       !CU_add_test(pSuite, "session_recv_premature_headers",
@@ -400,6 +402,7 @@ int main() {
                    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, "hd_huff_decode", test_nghttp2_hd_huff_decode) ||
       !CU_add_test(pSuite, "adjust_local_window_size",
                    test_nghttp2_adjust_local_window_size) ||
       !CU_add_test(pSuite, "check_header_name",
index 0408bf5..74725d4 100644 (file)
@@ -1538,3 +1538,40 @@ void test_nghttp2_hd_huff_encode(void) {
 
   nghttp2_bufs_free(&bufs);
 }
+
+void test_nghttp2_hd_huff_decode(void) {
+  const uint8_t e[] = {0x1f, 0xff, 0xff, 0xff, 0xff, 0xff};
+  nghttp2_hd_huff_decode_context ctx;
+  nghttp2_buf outbuf;
+  uint8_t b[256];
+  ssize_t len;
+
+  nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
+  nghttp2_hd_huff_decode_context_init(&ctx);
+  len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 1, 1);
+
+  CU_ASSERT(1 == len);
+  CU_ASSERT(0 == memcmp("a", outbuf.pos, 1));
+
+  /* Premature sequence must elicit decoding error */
+  nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
+  nghttp2_hd_huff_decode_context_init(&ctx);
+  len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 2, 1);
+
+  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == len);
+
+  /* Fully decoding EOS is error */
+  nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
+  nghttp2_hd_huff_decode_context_init(&ctx);
+  len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 2, 6);
+
+  CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == len);
+
+  /* Check failure state */
+  nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
+  nghttp2_hd_huff_decode_context_init(&ctx);
+  len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 5, 0);
+
+  CU_ASSERT(5 == len);
+  CU_ASSERT(nghttp2_hd_huff_decode_failure_state(&ctx));
+}
index 858afe6..ab0117c 100644 (file)
@@ -50,5 +50,6 @@ 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);
+void test_nghttp2_hd_huff_decode(void);
 
 #endif /* NGHTTP2_HD_TEST_H */
index 296fb9d..b366a6a 100644 (file)
@@ -1743,6 +1743,91 @@ void test_nghttp2_session_recv_headers_early_response(void) {
   nghttp2_bufs_free(&bufs);
 }
 
+void test_nghttp2_session_recv_headers_for_closed_stream(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_stream *stream;
+  nghttp2_mem *mem;
+  const uint8_t *data;
+
+  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.on_header_callback = on_header_callback;
+
+  nghttp2_session_server_new(&session, &callbacks, &ud);
+
+  nghttp2_hd_deflate_init(&deflater, mem);
+
+  /* Make sure that on_header callback never be invoked for closed
+     stream */
+  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);
+
+  nghttp2_frame_headers_free(&frame.headers, mem);
+
+  buf = &bufs.head->buf;
+  assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
+
+  ud.header_cb_called = 0;
+  ud.frame_recv_cb_called = 0;
+
+  rv = nghttp2_session_mem_recv(session, buf->pos, NGHTTP2_FRAME_HDLEN);
+
+  CU_ASSERT(NGHTTP2_FRAME_HDLEN == rv);
+  CU_ASSERT(0 == ud.header_cb_called);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  stream = nghttp2_session_get_stream(session, 1);
+
+  CU_ASSERT(NULL != stream);
+
+  rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1,
+                                 NGHTTP2_NO_ERROR);
+
+  CU_ASSERT(0 == rv);
+
+  rv = nghttp2_session_mem_send(session, &data);
+
+  CU_ASSERT(rv > 0);
+
+  stream = nghttp2_session_get_stream(session, 1);
+
+  CU_ASSERT(NULL == stream);
+
+  ud.header_cb_called = 0;
+  ud.frame_recv_cb_called = 0;
+
+  rv = nghttp2_session_mem_recv(session, buf->pos + NGHTTP2_FRAME_HDLEN,
+                                nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN);
+
+  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN == rv);
+  CU_ASSERT(0 == ud.header_cb_called);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  nghttp2_bufs_free(&bufs);
+  nghttp2_hd_deflate_free(&deflater);
+  nghttp2_session_del(session);
+}
+
 void test_nghttp2_session_server_recv_push_response(void) {
   nghttp2_session *session;
   nghttp2_session_callbacks callbacks;
@@ -9917,7 +10002,7 @@ void test_nghttp2_session_flooding(void) {
 
   buf = &bufs.head->buf;
 
-  for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
+  for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) {
     CU_ASSERT(
         (ssize_t)nghttp2_buf_len(buf) ==
         nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
@@ -9939,7 +10024,7 @@ void test_nghttp2_session_flooding(void) {
 
   buf = &bufs.head->buf;
 
-  for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
+  for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) {
     CU_ASSERT(
         (ssize_t)nghttp2_buf_len(buf) ==
         nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
@@ -11203,6 +11288,8 @@ void test_nghttp2_http_content_length_mismatch(void) {
       MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"),
       MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"),
       MAKE_NV("content-length", "20")};
+  const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"),
+                                 MAKE_NV("content-length", "20")};
   nghttp2_outbound_item *item;
   nghttp2_frame_hd hd;
 
@@ -11280,7 +11367,97 @@ void test_nghttp2_http_content_length_mismatch(void) {
 
   nghttp2_session_del(session);
 
+  /* Check for client */
+  nghttp2_session_client_new(&session, &callbacks, NULL);
+
+  nghttp2_hd_deflate_init(&deflater, mem);
+
+  /* header says content-length: 20, but HEADERS has END_STREAM flag set */
+  nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL);
+
+  CU_ASSERT(0 == nghttp2_session_send(session));
+
+  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);
+
+  item = nghttp2_session_get_next_ob_item(session);
+  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
+
+  CU_ASSERT(NULL != nghttp2_session_get_stream(session, 1));
+  CU_ASSERT(0 == nghttp2_session_send(session));
+  /* After sending RST_STREAM, stream must be closed */
+  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1));
+
+  nghttp2_bufs_reset(&bufs);
+
+  /* header says content-length: 20, but DATA has 0 byte */
+  nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL);
+
+  CU_ASSERT(0 == nghttp2_session_send(session));
+
+  rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, cl_resnv,
+                    ARRLEN(cl_resnv), 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(NULL != nghttp2_session_get_stream(session, 3));
+  CU_ASSERT(0 == nghttp2_session_send(session));
+  /* After sending RST_STREAM, stream must be closed */
+  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 3));
+
+  nghttp2_bufs_reset(&bufs);
+
+  /* header says content-length: 20, but DATA has 21 bytes */
+  nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, NULL);
+
+  CU_ASSERT(0 == nghttp2_session_send(session));
+
+  rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, cl_resnv,
+                    ARRLEN(cl_resnv), 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(NULL != nghttp2_session_get_stream(session, 5));
+  CU_ASSERT(0 == nghttp2_session_send(session));
+  /* After sending RST_STREAM, stream must be closed */
+  CU_ASSERT(NULL == nghttp2_session_get_stream(session, 5));
+
+  nghttp2_bufs_reset(&bufs);
+
   nghttp2_bufs_free(&bufs);
+
+  nghttp2_hd_deflate_free(&deflater);
+
+  nghttp2_session_del(session);
 }
 
 void test_nghttp2_http_non_final_response(void) {
@@ -11677,6 +11854,8 @@ void test_nghttp2_http_ignore_content_length(void) {
   const nghttp2_nv conn_reqnv[] = {MAKE_NV(":authority", "localhost"),
                                    MAKE_NV(":method", "CONNECT"),
                                    MAKE_NV("content-length", "999999")};
+  const nghttp2_nv conn_cl_resnv[] = {MAKE_NV(":status", "200"),
+                                      MAKE_NV("content-length", "0")};
   nghttp2_stream *stream;
 
   mem = nghttp2_mem_default();
@@ -11706,6 +11885,24 @@ void test_nghttp2_http_ignore_content_length(void) {
 
   nghttp2_bufs_reset(&bufs);
 
+  /* Content-Length in 200 response to CONNECT is ignored */
+  stream = open_sent_stream2(session, 3, NGHTTP2_STREAM_OPENING);
+  stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
+
+  rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS,
+                    conn_cl_resnv, ARRLEN(conn_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(-1 == stream->content_length);
+
+  nghttp2_bufs_reset(&bufs);
+
   nghttp2_hd_deflate_free(&deflater);
   nghttp2_session_del(session);
 
@@ -11781,13 +11978,10 @@ void test_nghttp2_http_record_request_method(void) {
   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 */
+  /* content-length is ignored 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);
+  CU_ASSERT(NULL == item);
 
   nghttp2_hd_deflate_free(&deflater);
   nghttp2_session_del(session);
index 35a48b8..e872c5d 100644 (file)
@@ -39,6 +39,7 @@ 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_recv_headers_for_closed_stream(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);
index a22b066..bf3adf8 100644 (file)
@@ -110,7 +110,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -185,7 +185,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index 7578a85..2f8270d 100644 (file)
@@ -1,9 +1,22 @@
 if(ENABLE_THIRD_PARTY)
-  set(LIBHTTP_PARSER_SOURCES
-    http-parser/http_parser.c
+  set(LIBLLHTTP_SOURCES
+    llhttp/src/api.c
+    llhttp/src/http.c
+    llhttp/src/llhttp.c
   )
-  add_library(http-parser OBJECT ${LIBHTTP_PARSER_SOURCES})
-  set_target_properties(http-parser PROPERTIES
+  add_library(llhttp OBJECT ${LIBLLHTTP_SOURCES})
+  target_include_directories(llhttp PRIVATE
+    "${CMAKE_CURRENT_SOURCE_DIR}/llhttp/include"
+  )
+  set_target_properties(llhttp PROPERTIES
+    POSITION_INDEPENDENT_CODE ON
+  )
+
+  set(LIBURL_PARSER_SOURCES
+    url-parser/url_parser.c
+  )
+  add_library(url-parser OBJECT ${LIBURL_PARSER_SOURCES})
+  set_target_properties(url-parser PROPERTIES
     POSITION_INDEPENDENT_CODE ON)
 
   if(HAVE_NEVERBLEED)
index ebc4945..5925236 100644 (file)
@@ -27,10 +27,18 @@ 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
+noinst_LTLIBRARIES = liburl-parser.la
+liburl_parser_la_SOURCES = \
+       url-parser/url_parser.c \
+       url-parser/url_parser.h
+
+noinst_LTLIBRARIES += libllhttp.la
+libllhttp_la_SOURCES = \
+       llhttp/src/api.c \
+       llhttp/src/http.c \
+       llhttp/src/llhttp.c \
+       llhttp/include/llhttp.h
+libllhttp_la_CPPFLAGS = -I${srcdir}/llhttp/include
 
 if HAVE_NEVERBLEED
 noinst_LTLIBRARIES += libneverbleed.la
@@ -49,8 +57,9 @@ 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}" \
+       CC="${CC}" CXX="$(firstword $(CXX))" LD="${LD}" \
+       CFLAGS="${CPPFLAGS} ${CFLAGS}" \
+       CXXFLAGS="$(wordlist 2, $(words $(CXX)), $(CXX)) ${CPPFLAGS} ${CXXFLAGS}" \
        LDFLAGS="${LDFLAGS}" \
        "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile"
 
index 19ab4fd..2d3d8dc 100644 (file)
@@ -121,7 +121,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
        $(top_srcdir)/m4/ax_boost_system.m4 \
        $(top_srcdir)/m4/ax_boost_thread.m4 \
        $(top_srcdir)/m4/ax_check_compile_flag.m4 \
-       $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
+       $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
        $(top_srcdir)/m4/ax_python_devel.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
        $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
@@ -134,24 +134,33 @@ CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 LTLIBRARIES = $(noinst_LTLIBRARIES)
-libhttp_parser_la_LIBADD =
-am__libhttp_parser_la_SOURCES_DIST = http-parser/http_parser.c \
-       http-parser/http_parser.h
+libllhttp_la_LIBADD =
+am__libllhttp_la_SOURCES_DIST = llhttp/src/api.c llhttp/src/http.c \
+       llhttp/src/llhttp.c llhttp/include/llhttp.h
 am__dirstamp = $(am__leading_dot)dirstamp
-@ENABLE_THIRD_PARTY_TRUE@am_libhttp_parser_la_OBJECTS =  \
-@ENABLE_THIRD_PARTY_TRUE@      http-parser/http_parser.lo
-libhttp_parser_la_OBJECTS = $(am_libhttp_parser_la_OBJECTS)
+@ENABLE_THIRD_PARTY_TRUE@am_libllhttp_la_OBJECTS =  \
+@ENABLE_THIRD_PARTY_TRUE@      llhttp/src/libllhttp_la-api.lo \
+@ENABLE_THIRD_PARTY_TRUE@      llhttp/src/libllhttp_la-http.lo \
+@ENABLE_THIRD_PARTY_TRUE@      llhttp/src/libllhttp_la-llhttp.lo
+libllhttp_la_OBJECTS = $(am_libllhttp_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
 am__v_lt_1 = 
-@ENABLE_THIRD_PARTY_TRUE@am_libhttp_parser_la_rpath =
+@ENABLE_THIRD_PARTY_TRUE@am_libllhttp_la_rpath =
 libneverbleed_la_DEPENDENCIES =
 am__libneverbleed_la_SOURCES_DIST = neverbleed/neverbleed.c \
        neverbleed/neverbleed.h
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@am_libneverbleed_la_OBJECTS = neverbleed/libneverbleed_la-neverbleed.lo
 libneverbleed_la_OBJECTS = $(am_libneverbleed_la_OBJECTS)
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@am_libneverbleed_la_rpath =
+liburl_parser_la_LIBADD =
+am__liburl_parser_la_SOURCES_DIST = url-parser/url_parser.c \
+       url-parser/url_parser.h
+@ENABLE_THIRD_PARTY_TRUE@am_liburl_parser_la_OBJECTS =  \
+@ENABLE_THIRD_PARTY_TRUE@      url-parser/url_parser.lo
+liburl_parser_la_OBJECTS = $(am_liburl_parser_la_OBJECTS)
+@ENABLE_THIRD_PARTY_TRUE@am_liburl_parser_la_rpath =
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -167,8 +176,11 @@ am__v_at_1 =
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__maybe_remake_depfiles = depfiles
-am__depfiles_remade = http-parser/$(DEPDIR)/http_parser.Plo \
-       neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo
+am__depfiles_remade = llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo \
+       llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo \
+       llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo \
+       neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo \
+       url-parser/$(DEPDIR)/url_parser.Plo
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -188,9 +200,11 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libhttp_parser_la_SOURCES) $(libneverbleed_la_SOURCES)
-DIST_SOURCES = $(am__libhttp_parser_la_SOURCES_DIST) \
-       $(am__libneverbleed_la_SOURCES_DIST)
+SOURCES = $(libllhttp_la_SOURCES) $(libneverbleed_la_SOURCES) \
+       $(liburl_parser_la_SOURCES)
+DIST_SOURCES = $(am__libllhttp_la_SOURCES_DIST) \
+       $(am__libneverbleed_la_SOURCES_DIST) \
+       $(am__liburl_parser_la_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -258,7 +272,7 @@ EXEEXT = @EXEEXT@
 EXTRACFLAG = @EXTRACFLAG@
 FGREP = @FGREP@
 GREP = @GREP@
-HAVE_CXX11 = @HAVE_CXX11@
+HAVE_CXX14 = @HAVE_CXX14@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -399,12 +413,19 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AM_CPPFLAGS = @DEFS@
 EXTRA_DIST = CMakeLists.txt $(am__append_2)
-@ENABLE_THIRD_PARTY_TRUE@noinst_LTLIBRARIES = libhttp-parser.la \
-@ENABLE_THIRD_PARTY_TRUE@      $(am__append_1)
-@ENABLE_THIRD_PARTY_TRUE@libhttp_parser_la_SOURCES = \
-@ENABLE_THIRD_PARTY_TRUE@      http-parser/http_parser.c \
-@ENABLE_THIRD_PARTY_TRUE@      http-parser/http_parser.h
-
+@ENABLE_THIRD_PARTY_TRUE@noinst_LTLIBRARIES = liburl-parser.la \
+@ENABLE_THIRD_PARTY_TRUE@      libllhttp.la $(am__append_1)
+@ENABLE_THIRD_PARTY_TRUE@liburl_parser_la_SOURCES = \
+@ENABLE_THIRD_PARTY_TRUE@      url-parser/url_parser.c \
+@ENABLE_THIRD_PARTY_TRUE@      url-parser/url_parser.h
+
+@ENABLE_THIRD_PARTY_TRUE@libllhttp_la_SOURCES = \
+@ENABLE_THIRD_PARTY_TRUE@      llhttp/src/api.c \
+@ENABLE_THIRD_PARTY_TRUE@      llhttp/src/http.c \
+@ENABLE_THIRD_PARTY_TRUE@      llhttp/src/llhttp.c \
+@ENABLE_THIRD_PARTY_TRUE@      llhttp/include/llhttp.h
+
+@ENABLE_THIRD_PARTY_TRUE@libllhttp_la_CPPFLAGS = -I${srcdir}/llhttp/include
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@libneverbleed_la_CPPFLAGS = @OPENSSL_CFLAGS@
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@libneverbleed_la_LIBADD = @OPENSSL_LIBS@
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_NEVERBLEED_TRUE@libneverbleed_la_SOURCES = neverbleed/neverbleed.c neverbleed/neverbleed.h
@@ -452,17 +473,21 @@ clean-noinstLTLIBRARIES:
          echo rm -f $${locs}; \
          rm -f $${locs}; \
        }
-http-parser/$(am__dirstamp):
-       @$(MKDIR_P) http-parser
-       @: > http-parser/$(am__dirstamp)
-http-parser/$(DEPDIR)/$(am__dirstamp):
-       @$(MKDIR_P) http-parser/$(DEPDIR)
-       @: > http-parser/$(DEPDIR)/$(am__dirstamp)
-http-parser/http_parser.lo: http-parser/$(am__dirstamp) \
-       http-parser/$(DEPDIR)/$(am__dirstamp)
-
-libhttp-parser.la: $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_DEPENDENCIES) $(EXTRA_libhttp_parser_la_DEPENDENCIES) 
-       $(AM_V_CCLD)$(LINK) $(am_libhttp_parser_la_rpath) $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_LIBADD) $(LIBS)
+llhttp/src/$(am__dirstamp):
+       @$(MKDIR_P) llhttp/src
+       @: > llhttp/src/$(am__dirstamp)
+llhttp/src/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) llhttp/src/$(DEPDIR)
+       @: > llhttp/src/$(DEPDIR)/$(am__dirstamp)
+llhttp/src/libllhttp_la-api.lo: llhttp/src/$(am__dirstamp) \
+       llhttp/src/$(DEPDIR)/$(am__dirstamp)
+llhttp/src/libllhttp_la-http.lo: llhttp/src/$(am__dirstamp) \
+       llhttp/src/$(DEPDIR)/$(am__dirstamp)
+llhttp/src/libllhttp_la-llhttp.lo: llhttp/src/$(am__dirstamp) \
+       llhttp/src/$(DEPDIR)/$(am__dirstamp)
+
+libllhttp.la: $(libllhttp_la_OBJECTS) $(libllhttp_la_DEPENDENCIES) $(EXTRA_libllhttp_la_DEPENDENCIES) 
+       $(AM_V_CCLD)$(LINK) $(am_libllhttp_la_rpath) $(libllhttp_la_OBJECTS) $(libllhttp_la_LIBADD) $(LIBS)
 neverbleed/$(am__dirstamp):
        @$(MKDIR_P) neverbleed
        @: > neverbleed/$(am__dirstamp)
@@ -474,19 +499,35 @@ neverbleed/libneverbleed_la-neverbleed.lo: neverbleed/$(am__dirstamp) \
 
 libneverbleed.la: $(libneverbleed_la_OBJECTS) $(libneverbleed_la_DEPENDENCIES) $(EXTRA_libneverbleed_la_DEPENDENCIES) 
        $(AM_V_CCLD)$(LINK) $(am_libneverbleed_la_rpath) $(libneverbleed_la_OBJECTS) $(libneverbleed_la_LIBADD) $(LIBS)
+url-parser/$(am__dirstamp):
+       @$(MKDIR_P) url-parser
+       @: > url-parser/$(am__dirstamp)
+url-parser/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) url-parser/$(DEPDIR)
+       @: > url-parser/$(DEPDIR)/$(am__dirstamp)
+url-parser/url_parser.lo: url-parser/$(am__dirstamp) \
+       url-parser/$(DEPDIR)/$(am__dirstamp)
+
+liburl-parser.la: $(liburl_parser_la_OBJECTS) $(liburl_parser_la_DEPENDENCIES) $(EXTRA_liburl_parser_la_DEPENDENCIES) 
+       $(AM_V_CCLD)$(LINK) $(am_liburl_parser_la_rpath) $(liburl_parser_la_OBJECTS) $(liburl_parser_la_LIBADD) $(LIBS)
 
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
-       -rm -f http-parser/*.$(OBJEXT)
-       -rm -f http-parser/*.lo
+       -rm -f llhttp/src/*.$(OBJEXT)
+       -rm -f llhttp/src/*.lo
        -rm -f neverbleed/*.$(OBJEXT)
        -rm -f neverbleed/*.lo
+       -rm -f url-parser/*.$(OBJEXT)
+       -rm -f url-parser/*.lo
 
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@http-parser/$(DEPDIR)/http_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@url-parser/$(DEPDIR)/url_parser.Plo@am__quote@ # am--include-marker
 
 $(am__depfiles_remade):
        @$(MKDIR_P) $(@D)
@@ -518,6 +559,27 @@ am--depfiles: $(am__depfiles_remade)
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
+llhttp/src/libllhttp_la-api.lo: llhttp/src/api.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT llhttp/src/libllhttp_la-api.lo -MD -MP -MF llhttp/src/$(DEPDIR)/libllhttp_la-api.Tpo -c -o llhttp/src/libllhttp_la-api.lo `test -f 'llhttp/src/api.c' || echo '$(srcdir)/'`llhttp/src/api.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) llhttp/src/$(DEPDIR)/libllhttp_la-api.Tpo llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='llhttp/src/api.c' object='llhttp/src/libllhttp_la-api.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o llhttp/src/libllhttp_la-api.lo `test -f 'llhttp/src/api.c' || echo '$(srcdir)/'`llhttp/src/api.c
+
+llhttp/src/libllhttp_la-http.lo: llhttp/src/http.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT llhttp/src/libllhttp_la-http.lo -MD -MP -MF llhttp/src/$(DEPDIR)/libllhttp_la-http.Tpo -c -o llhttp/src/libllhttp_la-http.lo `test -f 'llhttp/src/http.c' || echo '$(srcdir)/'`llhttp/src/http.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) llhttp/src/$(DEPDIR)/libllhttp_la-http.Tpo llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='llhttp/src/http.c' object='llhttp/src/libllhttp_la-http.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o llhttp/src/libllhttp_la-http.lo `test -f 'llhttp/src/http.c' || echo '$(srcdir)/'`llhttp/src/http.c
+
+llhttp/src/libllhttp_la-llhttp.lo: llhttp/src/llhttp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT llhttp/src/libllhttp_la-llhttp.lo -MD -MP -MF llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Tpo -c -o llhttp/src/libllhttp_la-llhttp.lo `test -f 'llhttp/src/llhttp.c' || echo '$(srcdir)/'`llhttp/src/llhttp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Tpo llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='llhttp/src/llhttp.c' object='llhttp/src/libllhttp_la-llhttp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libllhttp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o llhttp/src/libllhttp_la-llhttp.lo `test -f 'llhttp/src/llhttp.c' || echo '$(srcdir)/'`llhttp/src/llhttp.c
+
 neverbleed/libneverbleed_la-neverbleed.lo: neverbleed/neverbleed.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libneverbleed_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT neverbleed/libneverbleed_la-neverbleed.lo -MD -MP -MF neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Tpo -c -o neverbleed/libneverbleed_la-neverbleed.lo `test -f 'neverbleed/neverbleed.c' || echo '$(srcdir)/'`neverbleed/neverbleed.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Tpo neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo
@@ -530,8 +592,9 @@ mostlyclean-libtool:
 
 clean-libtool:
        -rm -rf .libs _libs
-       -rm -rf http-parser/.libs http-parser/_libs
+       -rm -rf llhttp/src/.libs llhttp/src/_libs
        -rm -rf neverbleed/.libs neverbleed/_libs
+       -rm -rf url-parser/.libs url-parser/_libs
 
 ID: $(am__tagged_files)
        $(am__define_uniq_tagged_files); mkid -fID $$unique
@@ -650,10 +713,12 @@ clean-generic:
 distclean-generic:
        -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
        -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-       -rm -f http-parser/$(DEPDIR)/$(am__dirstamp)
-       -rm -f http-parser/$(am__dirstamp)
+       -rm -f llhttp/src/$(DEPDIR)/$(am__dirstamp)
+       -rm -f llhttp/src/$(am__dirstamp)
        -rm -f neverbleed/$(DEPDIR)/$(am__dirstamp)
        -rm -f neverbleed/$(am__dirstamp)
+       -rm -f url-parser/$(DEPDIR)/$(am__dirstamp)
+       -rm -f url-parser/$(am__dirstamp)
 
 maintainer-clean-generic:
        @echo "This command is intended for maintainers to use"
@@ -666,8 +731,11 @@ clean-am: clean-generic clean-libtool clean-local \
        clean-noinstLTLIBRARIES mostlyclean-am
 
 distclean: distclean-am
-               -rm -f http-parser/$(DEPDIR)/http_parser.Plo
+               -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo
+       -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo
+       -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo
        -rm -f neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo
+       -rm -f url-parser/$(DEPDIR)/url_parser.Plo
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
@@ -713,8 +781,11 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-               -rm -f http-parser/$(DEPDIR)/http_parser.Plo
+               -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-api.Plo
+       -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-http.Plo
+       -rm -f llhttp/src/$(DEPDIR)/libllhttp_la-llhttp.Plo
        -rm -f neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo
+       -rm -f url-parser/$(DEPDIR)/url_parser.Plo
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -758,8 +829,9 @@ uninstall-am:
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     MRUBY_CONFIG="${srcdir}/build_config.rb" \
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     BUILD_DIR="${abs_builddir}/mruby/build" \
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     INSTALL_DIR="${abs_builddir}/mruby/build/install/bin" \
-@ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     CC="${CC}" CXX="${CXX}" LD="${LD}" \
-@ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     CFLAGS="${CPPFLAGS} ${CFLAGS}" CXXFLAGS="${CPPFLAGS} ${CXXFLAGS}" \
+@ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     CC="${CC}" CXX="$(firstword $(CXX))" LD="${LD}" \
+@ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     CFLAGS="${CPPFLAGS} ${CFLAGS}" \
+@ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     CXXFLAGS="$(wordlist 2, $(words $(CXX)), $(CXX)) ${CPPFLAGS} ${CXXFLAGS}" \
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     LDFLAGS="${LDFLAGS}" \
 @ENABLE_THIRD_PARTY_TRUE@@HAVE_MRUBY_TRUE@     "${srcdir}/mruby/minirake" -f "${srcdir}/mruby/Rakefile"
 
diff --git a/third-party/http-parser/http_parser.c b/third-party/http-parser/http_parser.c
deleted file mode 100644 (file)
index 895bf0c..0000000
+++ /dev/null
@@ -1,2470 +0,0 @@
-/* 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;
-}
diff --git a/third-party/http-parser/http_parser.h b/third-party/http-parser/http_parser.h
deleted file mode 100644 (file)
index ea26394..0000000
+++ /dev/null
@@ -1,362 +0,0 @@
-/* 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
diff --git a/third-party/llhttp/include/llhttp.h b/third-party/llhttp/include/llhttp.h
new file mode 100644 (file)
index 0000000..f9e008c
--- /dev/null
@@ -0,0 +1,361 @@
+#ifndef INCLUDE_LLHTTP_H_
+#define INCLUDE_LLHTTP_H_
+
+#define LLHTTP_VERSION_MAJOR 1
+#define LLHTTP_VERSION_MINOR 1
+#define LLHTTP_VERSION_PATCH 3
+
+#ifndef INCLUDE_LLHTTP_ITSELF_H_
+#define INCLUDE_LLHTTP_ITSELF_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+typedef struct llhttp__internal_s llhttp__internal_t;
+struct llhttp__internal_s {
+  int32_t _index;
+  void* _span_pos0;
+  void* _span_cb0;
+  int32_t error;
+  const char* reason;
+  const char* error_pos;
+  void* data;
+  void* _current;
+  uint64_t content_length;
+  uint8_t type;
+  uint8_t method;
+  uint8_t http_major;
+  uint8_t http_minor;
+  uint8_t header_state;
+  uint8_t flags;
+  uint8_t upgrade;
+  uint16_t status_code;
+  uint8_t finish;
+  void* settings;
+};
+
+int llhttp__internal_init(llhttp__internal_t* s);
+int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* INCLUDE_LLHTTP_ITSELF_H_ */
+
+#ifndef LLLLHTTP_C_HEADERS_
+#define LLLLHTTP_C_HEADERS_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum llhttp_errno {
+  HPE_OK = 0,
+  HPE_INTERNAL = 1,
+  HPE_STRICT = 2,
+  HPE_LF_EXPECTED = 3,
+  HPE_UNEXPECTED_CONTENT_LENGTH = 4,
+  HPE_CLOSED_CONNECTION = 5,
+  HPE_INVALID_METHOD = 6,
+  HPE_INVALID_URL = 7,
+  HPE_INVALID_CONSTANT = 8,
+  HPE_INVALID_VERSION = 9,
+  HPE_INVALID_HEADER_TOKEN = 10,
+  HPE_INVALID_CONTENT_LENGTH = 11,
+  HPE_INVALID_CHUNK_SIZE = 12,
+  HPE_INVALID_STATUS = 13,
+  HPE_INVALID_EOF_STATE = 14,
+  HPE_CB_MESSAGE_BEGIN = 15,
+  HPE_CB_HEADERS_COMPLETE = 16,
+  HPE_CB_MESSAGE_COMPLETE = 17,
+  HPE_CB_CHUNK_HEADER = 18,
+  HPE_CB_CHUNK_COMPLETE = 19,
+  HPE_PAUSED = 20,
+  HPE_PAUSED_UPGRADE = 21,
+  HPE_USER = 22
+};
+typedef enum llhttp_errno llhttp_errno_t;
+
+enum llhttp_flags {
+  F_CONNECTION_KEEP_ALIVE = 0x1,
+  F_CONNECTION_CLOSE = 0x2,
+  F_CONNECTION_UPGRADE = 0x4,
+  F_CHUNKED = 0x8,
+  F_UPGRADE = 0x10,
+  F_CONTENT_LENGTH = 0x20,
+  F_SKIPBODY = 0x40,
+  F_TRAILING = 0x80
+};
+typedef enum llhttp_flags llhttp_flags_t;
+
+enum llhttp_type {
+  HTTP_BOTH = 0,
+  HTTP_REQUEST = 1,
+  HTTP_RESPONSE = 2
+};
+typedef enum llhttp_type llhttp_type_t;
+
+enum llhttp_finish {
+  HTTP_FINISH_SAFE = 0,
+  HTTP_FINISH_SAFE_WITH_CB = 1,
+  HTTP_FINISH_UNSAFE = 2
+};
+typedef enum llhttp_finish llhttp_finish_t;
+
+enum llhttp_method {
+  HTTP_DELETE = 0,
+  HTTP_GET = 1,
+  HTTP_HEAD = 2,
+  HTTP_POST = 3,
+  HTTP_PUT = 4,
+  HTTP_CONNECT = 5,
+  HTTP_OPTIONS = 6,
+  HTTP_TRACE = 7,
+  HTTP_COPY = 8,
+  HTTP_LOCK = 9,
+  HTTP_MKCOL = 10,
+  HTTP_MOVE = 11,
+  HTTP_PROPFIND = 12,
+  HTTP_PROPPATCH = 13,
+  HTTP_SEARCH = 14,
+  HTTP_UNLOCK = 15,
+  HTTP_BIND = 16,
+  HTTP_REBIND = 17,
+  HTTP_UNBIND = 18,
+  HTTP_ACL = 19,
+  HTTP_REPORT = 20,
+  HTTP_MKACTIVITY = 21,
+  HTTP_CHECKOUT = 22,
+  HTTP_MERGE = 23,
+  HTTP_MSEARCH = 24,
+  HTTP_NOTIFY = 25,
+  HTTP_SUBSCRIBE = 26,
+  HTTP_UNSUBSCRIBE = 27,
+  HTTP_PATCH = 28,
+  HTTP_PURGE = 29,
+  HTTP_MKCALENDAR = 30,
+  HTTP_LINK = 31,
+  HTTP_UNLINK = 32,
+  HTTP_SOURCE = 33
+};
+typedef enum llhttp_method llhttp_method_t;
+
+#define HTTP_ERRNO_MAP(XX) \
+  XX(0, OK, OK) \
+  XX(1, INTERNAL, INTERNAL) \
+  XX(2, STRICT, STRICT) \
+  XX(3, LF_EXPECTED, LF_EXPECTED) \
+  XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
+  XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
+  XX(6, INVALID_METHOD, INVALID_METHOD) \
+  XX(7, INVALID_URL, INVALID_URL) \
+  XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
+  XX(9, INVALID_VERSION, INVALID_VERSION) \
+  XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
+  XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
+  XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
+  XX(13, INVALID_STATUS, INVALID_STATUS) \
+  XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
+  XX(15, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
+  XX(16, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
+  XX(17, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
+  XX(18, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
+  XX(19, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
+  XX(20, PAUSED, PAUSED) \
+  XX(21, PAUSED_UPGRADE, PAUSED_UPGRADE) \
+  XX(22, USER, USER) \
+
+
+#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) \
+  XX(5, CONNECT, CONNECT) \
+  XX(6, OPTIONS, OPTIONS) \
+  XX(7, TRACE, TRACE) \
+  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) \
+  XX(20, REPORT, REPORT) \
+  XX(21, MKACTIVITY, MKACTIVITY) \
+  XX(22, CHECKOUT, CHECKOUT) \
+  XX(23, MERGE, MERGE) \
+  XX(24, MSEARCH, M-SEARCH) \
+  XX(25, NOTIFY, NOTIFY) \
+  XX(26, SUBSCRIBE, SUBSCRIBE) \
+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+  XX(28, PATCH, PATCH) \
+  XX(29, PURGE, PURGE) \
+  XX(30, MKCALENDAR, MKCALENDAR) \
+  XX(31, LINK, LINK) \
+  XX(32, UNLINK, UNLINK) \
+  XX(33, SOURCE, SOURCE) \
+
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* LLLLHTTP_C_HEADERS_ */
+
+#ifndef INCLUDE_LLHTTP_API_H_
+#define INCLUDE_LLHTTP_API_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stddef.h>
+
+typedef llhttp__internal_t llhttp_t;
+typedef struct llhttp_settings_s llhttp_settings_t;
+
+typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
+typedef int (*llhttp_cb)(llhttp_t*);
+
+struct llhttp_settings_s {
+  /* Possible return values 0, -1, `HPE_PAUSED` */
+  llhttp_cb      on_message_begin;
+
+  llhttp_data_cb on_url;
+  llhttp_data_cb on_status;
+  llhttp_data_cb on_header_field;
+  llhttp_data_cb on_header_value;
+
+  /* Possible return values:
+   * 0  - Proceed normally
+   * 1  - Assume that request/response has no body, and proceed to parsing the
+   *      next message
+   * 2  - Assume absence of body (as above) and make `llhttp_execute()` return
+   *      `HPE_PAUSED_UPGRADE`
+   * -1 - Error
+   * `HPE_PAUSED`
+   */
+  llhttp_cb      on_headers_complete;
+
+  llhttp_data_cb on_body;
+
+  /* Possible return values 0, -1, `HPE_PAUSED` */
+  llhttp_cb      on_message_complete;
+
+  /* When on_chunk_header is called, the current chunk length is stored
+   * in parser->content_length.
+   * Possible return values 0, -1, `HPE_PAUSED`
+   */
+  llhttp_cb      on_chunk_header;
+  llhttp_cb      on_chunk_complete;
+};
+
+/* Initialize the parser with specific type and user settings */
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+                 const llhttp_settings_t* settings);
+
+/* Initialize the settings object */
+void llhttp_settings_init(llhttp_settings_t* settings);
+
+/* Parse full or partial request/response, invoking user callbacks along the
+ * way.
+ *
+ * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
+ * interrupts, and such errno is returned from `llhttp_execute()`. If
+ * `HPE_PAUSED` was used as a errno, the execution can be resumed with
+ * `llhttp_resume()` call.
+ *
+ * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
+ * is returned after fully parsing the request/response. If the user wishes to
+ * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
+ *
+ * NOTE: if this function ever returns a non-pause type error, it will continue
+ * to return the same error upon each successive call up until `llhttp_init()`
+ * is called.
+ */
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
+
+/* This method should be called when the other side has no further bytes to
+ * send (e.g. shutdown of readable side of the TCP connection.)
+ *
+ * Requests without `Content-Length` and other messages might require treating
+ * all incoming bytes as the part of the body, up to the last byte of the
+ * connection. This method will invoke `on_message_complete()` callback if the
+ * request was terminated safely. Otherwise a error code would be returned.
+ */
+llhttp_errno_t llhttp_finish(llhttp_t* parser);
+
+/* Returns `1` if the incoming message is parsed until the last byte, and has
+ * to be completed by calling `llhttp_finish()` on EOF
+ */
+int llhttp_message_needs_eof(const llhttp_t* parser);
+
+/* Returns `1` if there might be any other messages following the last that was
+ * successfuly parsed.
+ */
+int llhttp_should_keep_alive(const llhttp_t* parser);
+
+/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
+ * appropriate error reason.
+ *
+ * Important: do not call this from user callbacks! User callbacks must return
+ * `HPE_PAUSED` if pausing is required.
+ */
+void llhttp_pause(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
+ */
+void llhttp_resume(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
+ */
+void llhttp_resume_after_upgrade(llhttp_t* parser);
+
+/* Returns the latest return error */
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
+
+/* Returns the verbal explanation of the latest returned error.
+ *
+ * Note: User callback should set error reason when returning the error. See
+ * `llhttp_set_error_reason()` for details.
+ */
+const char* llhttp_get_error_reason(const llhttp_t* parser);
+
+/* Assign verbal description to the returned error. Must be called in user
+ * callbacks right before returning the errno.
+ *
+ * Note: `HPE_USER` error code might be useful in user callbacks.
+ */
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
+
+/* Returns the pointer to the last parsed byte before the returned error. The
+ * pointer is relative to the `data` argument of `llhttp_execute()`.
+ *
+ * Note: this method might be useful for counting the number of parsed bytes.
+ */
+const char* llhttp_get_error_pos(const llhttp_t* parser);
+
+/* Returns textual name of error code */
+const char* llhttp_errno_name(llhttp_errno_t err);
+
+/* Returns textual name of HTTP method */
+const char* llhttp_method_name(llhttp_method_t method);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* INCLUDE_LLHTTP_API_H_ */
+
+#endif  /* INCLUDE_LLHTTP_H_ */
diff --git a/third-party/llhttp/src/api.c b/third-party/llhttp/src/api.c
new file mode 100644 (file)
index 0000000..45227b3
--- /dev/null
@@ -0,0 +1,215 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "llhttp.h"
+
+#define CALLBACK_MAYBE(PARSER, NAME, ...)                                     \
+  do {                                                                        \
+    llhttp_settings_t* settings;                                              \
+    settings = (llhttp_settings_t*) (PARSER)->settings;                       \
+    if (settings == NULL || settings->NAME == NULL) {                         \
+      err = 0;                                                                \
+      break;                                                                  \
+    }                                                                         \
+    err = settings->NAME(__VA_ARGS__);                                        \
+  } while (0)
+
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+                 const llhttp_settings_t* settings) {
+  llhttp__internal_init(parser);
+
+  parser->type = type;
+  parser->settings = (void*) settings;
+}
+
+
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
+  return llhttp__internal_execute(parser, data, data + len);
+}
+
+
+void llhttp_settings_init(llhttp_settings_t* settings) {
+  memset(settings, 0, sizeof(*settings));
+}
+
+
+llhttp_errno_t llhttp_finish(llhttp_t* parser) {
+  int err;
+
+  /* We're in an error state. Don't bother doing anything. */
+  if (parser->error != 0) {
+    return 0;
+  }
+
+  switch (parser->finish) {
+    case HTTP_FINISH_SAFE_WITH_CB:
+      CALLBACK_MAYBE(parser, on_message_complete, parser);
+      if (err != HPE_OK) return err;
+
+    /* FALLTHROUGH */
+    case HTTP_FINISH_SAFE:
+      return HPE_OK;
+    case HTTP_FINISH_UNSAFE:
+      parser->reason = "Invalid EOF state";
+      return HPE_INVALID_EOF_STATE;
+    default:
+      abort();
+  }
+}
+
+
+void llhttp_pause(llhttp_t* parser) {
+  if (parser->error != HPE_OK) {
+    return;
+  }
+
+  parser->error = HPE_PAUSED;
+  parser->reason = "Paused";
+}
+
+
+void llhttp_resume(llhttp_t* parser) {
+  if (parser->error != HPE_PAUSED) {
+    return;
+  }
+
+  parser->error = 0;
+}
+
+
+void llhttp_resume_after_upgrade(llhttp_t* parser) {
+  if (parser->error != HPE_PAUSED_UPGRADE) {
+    return;
+  }
+
+  parser->error = 0;
+}
+
+
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
+  return parser->error;
+}
+
+
+const char* llhttp_get_error_reason(const llhttp_t* parser) {
+  return parser->reason;
+}
+
+
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
+  parser->reason = reason;
+}
+
+
+const char* llhttp_get_error_pos(const llhttp_t* parser) {
+  return parser->error_pos;
+}
+
+
+const char* llhttp_errno_name(llhttp_errno_t err) {
+#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
+  switch (err) {
+    HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+    default: abort();
+  }
+#undef HTTP_ERRNO_GEN
+}
+
+
+const char* llhttp_method_name(llhttp_method_t method) {
+#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
+  switch (method) {
+    HTTP_METHOD_MAP(HTTP_METHOD_GEN)
+    default: abort();
+  }
+#undef HTTP_METHOD_GEN
+}
+
+
+/* Callbacks */
+
+
+int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_message_begin, s);
+  return err;
+}
+
+
+int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_url, s, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_status, s, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_header_field, s, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_header_value, s, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_headers_complete, s);
+  return err;
+}
+
+
+int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_message_complete, s);
+  return err;
+}
+
+
+int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_body, s, p, endp - p);
+  return err;
+}
+
+
+int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_chunk_header, s);
+  return err;
+}
+
+
+int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
+  int err;
+  CALLBACK_MAYBE(s, on_chunk_complete, s);
+  return err;
+}
+
+
+/* Private */
+
+
+void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
+                   const char* msg) {
+  if (p == endp) {
+    fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
+            s->flags, msg);
+  } else {
+    fprintf(stderr, "p=%p type=%d flags=%02x next=%02x   debug=%s\n", s,
+            s->type, s->flags, *p, msg);
+  }
+}
diff --git a/third-party/llhttp/src/http.c b/third-party/llhttp/src/http.c
new file mode 100644 (file)
index 0000000..67834c2
--- /dev/null
@@ -0,0 +1,120 @@
+#include <stdio.h>
+#ifndef LLHTTP__TEST
+# include "llhttp.h"
+#else
+# define llhttp_t llparse_t
+#endif  /* */
+
+int llhttp_message_needs_eof(const llhttp_t* parser);
+int llhttp_should_keep_alive(const llhttp_t* parser);
+
+int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
+                                    const char* endp) {
+  /* Set this here so that on_headers_complete() callbacks can see it */
+  if ((parser->flags & F_UPGRADE) &&
+      (parser->flags & F_CONNECTION_UPGRADE)) {
+    /* For responses, "Upgrade: foo" and "Connection: upgrade" are
+     * mandatory only when it is a 101 Switching Protocols response,
+     * otherwise it is purely informational, to announce support.
+     */
+    parser->upgrade =
+        (parser->type == HTTP_REQUEST || parser->status_code == 101);
+  } else {
+    parser->upgrade = (parser->method == HTTP_CONNECT);
+  }
+  return 0;
+}
+
+
+/* Return values:
+ * 0 - No body, `restart`, message_complete
+ * 1 - CONNECT request, `restart`, message_complete, and pause
+ * 2 - chunk_size_start
+ * 3 - body_identity
+ * 4 - body_identity_eof
+ */
+int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
+                                   const char* endp) {
+  int hasBody;
+
+  hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
+  if (parser->upgrade && (parser->method == HTTP_CONNECT ||
+                          (parser->flags & F_SKIPBODY) || !hasBody)) {
+    /* Exit, the rest of the message is in a different protocol. */
+    return 1;
+  }
+
+  if (parser->flags & F_SKIPBODY) {
+    return 0;
+  } else if (parser->flags & F_CHUNKED) {
+    /* chunked encoding - ignore Content-Length header */
+    return 2;
+  } else {
+    if (!(parser->flags & F_CONTENT_LENGTH)) {
+      if (!llhttp_message_needs_eof(parser)) {
+        /* Assume content-length 0 - read the next */
+        return 0;
+      } else {
+        /* Read body until EOF */
+        return 4;
+      }
+    } else if (parser->content_length == 0) {
+      /* Content-Length header given but zero: Content-Length: 0\r\n */
+      return 0;
+    } else {
+      /* Content-Length header given and non-zero */
+      return 3;
+    }
+  }
+}
+
+
+int llhttp__after_message_complete(llhttp_t* parser, const char* p,
+                                   const char* endp) {
+  int should_keep_alive;
+
+  should_keep_alive = llhttp_should_keep_alive(parser);
+  parser->flags = 0;
+  parser->finish = HTTP_FINISH_SAFE;
+
+  /* NOTE: this is ignored in loose parsing mode */
+  return should_keep_alive;
+}
+
+
+int llhttp_message_needs_eof(const llhttp_t* 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 | F_CONTENT_LENGTH)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+
+int llhttp_should_keep_alive(const llhttp_t* 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 !llhttp_message_needs_eof(parser);
+}
diff --git a/third-party/llhttp/src/llhttp.c b/third-party/llhttp/src/llhttp.c
new file mode 100644 (file)
index 0000000..dc363aa
--- /dev/null
@@ -0,0 +1,6178 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "llhttp.h"
+
+typedef int (*llhttp__internal__span_cb)(
+             llhttp__internal_t*, const char*, const char*);
+
+static const unsigned char llparse_blob0[] = {
+  'C', 'L'
+};
+static const unsigned char llparse_blob1[] = {
+  'o', 'n'
+};
+static const unsigned char llparse_blob2[] = {
+  'e', 'c', 't', 'i', 'o', 'n'
+};
+static const unsigned char llparse_blob3[] = {
+  'l', 'o', 's', 'e'
+};
+static const unsigned char llparse_blob4[] = {
+  'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e'
+};
+static const unsigned char llparse_blob5[] = {
+  'p', 'g', 'r', 'a', 'd', 'e'
+};
+static const unsigned char llparse_blob6[] = {
+  'h', 'u', 'n', 'k', 'e', 'd'
+};
+static const unsigned char llparse_blob7[] = {
+  'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h'
+};
+static const unsigned char llparse_blob8[] = {
+  'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c',
+  't', 'i', 'o', 'n'
+};
+static const unsigned char llparse_blob9[] = {
+  'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c',
+  'o', 'd', 'i', 'n', 'g'
+};
+static const unsigned char llparse_blob10[] = {
+  'p', 'g', 'r', 'a', 'd', 'e'
+};
+static const unsigned char llparse_blob11[] = {
+  0xd, 0xa
+};
+static const unsigned char llparse_blob12[] = {
+  'T', 'T', 'P', '/'
+};
+static const unsigned char llparse_blob13[] = {
+  'C', 'E', '/'
+};
+static const unsigned char llparse_blob14[] = {
+  'I', 'N', 'D'
+};
+static const unsigned char llparse_blob15[] = {
+  'E', 'C', 'K', 'O', 'U', 'T'
+};
+static const unsigned char llparse_blob16[] = {
+  'N', 'E', 'C', 'T'
+};
+static const unsigned char llparse_blob17[] = {
+  'E', 'L', 'E', 'T', 'E'
+};
+static const unsigned char llparse_blob18[] = {
+  'E', 'T'
+};
+static const unsigned char llparse_blob19[] = {
+  'E', 'A', 'D'
+};
+static const unsigned char llparse_blob20[] = {
+  'N', 'K'
+};
+static const unsigned char llparse_blob21[] = {
+  'C', 'K'
+};
+static const unsigned char llparse_blob22[] = {
+  'S', 'E', 'A', 'R', 'C', 'H'
+};
+static const unsigned char llparse_blob23[] = {
+  'R', 'G', 'E'
+};
+static const unsigned char llparse_blob24[] = {
+  'C', 'T', 'I', 'V', 'I', 'T', 'Y'
+};
+static const unsigned char llparse_blob25[] = {
+  'L', 'E', 'N', 'D', 'A', 'R'
+};
+static const unsigned char llparse_blob26[] = {
+  'V', 'E'
+};
+static const unsigned char llparse_blob27[] = {
+  'O', 'T', 'I', 'F', 'Y'
+};
+static const unsigned char llparse_blob28[] = {
+  'P', 'T', 'I', 'O', 'N', 'S'
+};
+static const unsigned char llparse_blob29[] = {
+  'T', 'C', 'H'
+};
+static const unsigned char llparse_blob30[] = {
+  'S', 'T'
+};
+static const unsigned char llparse_blob31[] = {
+  'O', 'P'
+};
+static const unsigned char llparse_blob32[] = {
+  'I', 'N', 'D'
+};
+static const unsigned char llparse_blob33[] = {
+  'A', 'T', 'C', 'H'
+};
+static const unsigned char llparse_blob34[] = {
+  'G', 'E'
+};
+static const unsigned char llparse_blob35[] = {
+  'I', 'N', 'D'
+};
+static const unsigned char llparse_blob36[] = {
+  'O', 'R', 'T'
+};
+static const unsigned char llparse_blob37[] = {
+  'A', 'R', 'C', 'H'
+};
+static const unsigned char llparse_blob38[] = {
+  'U', 'R', 'C', 'E'
+};
+static const unsigned char llparse_blob39[] = {
+  'B', 'S', 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob40[] = {
+  'R', 'A', 'C', 'E'
+};
+static const unsigned char llparse_blob41[] = {
+  'I', 'N', 'D'
+};
+static const unsigned char llparse_blob42[] = {
+  'N', 'K'
+};
+static const unsigned char llparse_blob43[] = {
+  'C', 'K'
+};
+static const unsigned char llparse_blob44[] = {
+  'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob45[] = {
+  'H', 'T', 'T', 'P', '/'
+};
+static const unsigned char llparse_blob46[] = {
+  'A', 'D'
+};
+static const unsigned char llparse_blob47[] = {
+  'T', 'P', '/'
+};
+
+enum llparse_match_status_e {
+  kMatchComplete,
+  kMatchPause,
+  kMatchMismatch
+};
+typedef enum llparse_match_status_e llparse_match_status_t;
+
+struct llparse_match_s {
+  llparse_match_status_t status;
+  const unsigned char* current;
+};
+typedef struct llparse_match_s llparse_match_t;
+
+static llparse_match_t llparse__match_sequence_id(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp,
+    const unsigned char* seq, uint32_t seq_len) {
+  uint32_t index;
+  llparse_match_t res;
+
+  index = s->_index;
+  for (; p != endp; p++) {
+    unsigned char current;
+
+    current = *p;
+    if (current == seq[index]) {
+      if (++index == seq_len) {
+        res.status = kMatchComplete;
+        goto reset;
+      }
+    } else {
+      res.status = kMatchMismatch;
+      goto reset;
+    }
+  }
+  s->_index = index;
+  res.status = kMatchPause;
+  res.current = p;
+  return res;
+reset:
+  s->_index = 0;
+  res.current = p;
+  return res;
+}
+
+static llparse_match_t llparse__match_sequence_to_lower_unsafe(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp,
+    const unsigned char* seq, uint32_t seq_len) {
+  uint32_t index;
+  llparse_match_t res;
+
+  index = s->_index;
+  for (; p != endp; p++) {
+    unsigned char current;
+
+    current = ((*p) | 0x20);
+    if (current == seq[index]) {
+      if (++index == seq_len) {
+        res.status = kMatchComplete;
+        goto reset;
+      }
+    } else {
+      res.status = kMatchMismatch;
+      goto reset;
+    }
+  }
+  s->_index = index;
+  res.status = kMatchPause;
+  res.current = p;
+  return res;
+reset:
+  s->_index = 0;
+  res.current = p;
+  return res;
+}
+
+enum llparse_state_e {
+  s_error,
+  s_n_llhttp__internal__n_invoke_llhttp__after_message_complete,
+  s_n_llhttp__internal__n_pause_1,
+  s_n_llhttp__internal__n_invoke_is_equal_upgrade,
+  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2,
+  s_n_llhttp__internal__n_chunk_data_almost_done_skip,
+  s_n_llhttp__internal__n_chunk_data_almost_done,
+  s_n_llhttp__internal__n_consume_content_length,
+  s_n_llhttp__internal__n_span_start_llhttp__on_body,
+  s_n_llhttp__internal__n_invoke_is_equal_content_length,
+  s_n_llhttp__internal__n_chunk_size_almost_done,
+  s_n_llhttp__internal__n_chunk_parameters,
+  s_n_llhttp__internal__n_chunk_size_otherwise,
+  s_n_llhttp__internal__n_chunk_size,
+  s_n_llhttp__internal__n_chunk_size_digit,
+  s_n_llhttp__internal__n_invoke_update_content_length,
+  s_n_llhttp__internal__n_consume_content_length_1,
+  s_n_llhttp__internal__n_span_start_llhttp__on_body_1,
+  s_n_llhttp__internal__n_eof,
+  s_n_llhttp__internal__n_span_start_llhttp__on_body_2,
+  s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete,
+  s_n_llhttp__internal__n_headers_almost_done,
+  s_n_llhttp__internal__n_span_start_llhttp__on_header_value,
+  s_n_llhttp__internal__n_header_value_discard_lws,
+  s_n_llhttp__internal__n_header_value_discard_ws_almost_done,
+  s_n_llhttp__internal__n_header_value_lws,
+  s_n_llhttp__internal__n_header_value_almost_done,
+  s_n_llhttp__internal__n_header_value_otherwise,
+  s_n_llhttp__internal__n_header_value_connection_token,
+  s_n_llhttp__internal__n_header_value_connection_ws,
+  s_n_llhttp__internal__n_header_value_connection_1,
+  s_n_llhttp__internal__n_header_value_connection_2,
+  s_n_llhttp__internal__n_header_value_connection_3,
+  s_n_llhttp__internal__n_header_value_connection,
+  s_n_llhttp__internal__n_error_15,
+  s_n_llhttp__internal__n_header_value,
+  s_n_llhttp__internal__n_header_value_discard_rws,
+  s_n_llhttp__internal__n_error_16,
+  s_n_llhttp__internal__n_header_value_content_length_ws,
+  s_n_llhttp__internal__n_header_value_content_length,
+  s_n_llhttp__internal__n_header_value_te_chunked_1,
+  s_n_llhttp__internal__n_header_value_te_chunked,
+  s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1,
+  s_n_llhttp__internal__n_header_value_discard_ws,
+  s_n_llhttp__internal__n_header_field_general_otherwise,
+  s_n_llhttp__internal__n_header_field_general,
+  s_n_llhttp__internal__n_header_field_colon,
+  s_n_llhttp__internal__n_header_field_3,
+  s_n_llhttp__internal__n_header_field_4,
+  s_n_llhttp__internal__n_header_field_2,
+  s_n_llhttp__internal__n_header_field_1,
+  s_n_llhttp__internal__n_header_field_5,
+  s_n_llhttp__internal__n_header_field_6,
+  s_n_llhttp__internal__n_header_field_7,
+  s_n_llhttp__internal__n_header_field,
+  s_n_llhttp__internal__n_span_start_llhttp__on_header_field,
+  s_n_llhttp__internal__n_header_field_start,
+  s_n_llhttp__internal__n_url_skip_to_http09,
+  s_n_llhttp__internal__n_url_skip_lf_to_http09,
+  s_n_llhttp__internal__n_req_http_end_1,
+  s_n_llhttp__internal__n_req_http_end,
+  s_n_llhttp__internal__n_req_http_minor,
+  s_n_llhttp__internal__n_req_http_dot,
+  s_n_llhttp__internal__n_req_http_major,
+  s_n_llhttp__internal__n_req_http_start_1,
+  s_n_llhttp__internal__n_req_http_start_2,
+  s_n_llhttp__internal__n_req_http_start,
+  s_n_llhttp__internal__n_url_skip_to_http,
+  s_n_llhttp__internal__n_url_fragment,
+  s_n_llhttp__internal__n_span_end_stub_query_3,
+  s_n_llhttp__internal__n_url_query,
+  s_n_llhttp__internal__n_url_query_or_fragment,
+  s_n_llhttp__internal__n_url_path,
+  s_n_llhttp__internal__n_span_start_stub_path_2,
+  s_n_llhttp__internal__n_span_start_stub_path,
+  s_n_llhttp__internal__n_span_start_stub_path_1,
+  s_n_llhttp__internal__n_url_server_with_at,
+  s_n_llhttp__internal__n_url_server,
+  s_n_llhttp__internal__n_url_schema_delim_1,
+  s_n_llhttp__internal__n_url_schema_delim,
+  s_n_llhttp__internal__n_span_end_stub_schema,
+  s_n_llhttp__internal__n_url_schema,
+  s_n_llhttp__internal__n_url_start,
+  s_n_llhttp__internal__n_span_start_llhttp__on_url_1,
+  s_n_llhttp__internal__n_span_start_llhttp__on_url,
+  s_n_llhttp__internal__n_req_spaces_before_url,
+  s_n_llhttp__internal__n_req_first_space_before_url,
+  s_n_llhttp__internal__n_start_req_1,
+  s_n_llhttp__internal__n_start_req_2,
+  s_n_llhttp__internal__n_start_req_4,
+  s_n_llhttp__internal__n_start_req_6,
+  s_n_llhttp__internal__n_start_req_7,
+  s_n_llhttp__internal__n_start_req_5,
+  s_n_llhttp__internal__n_start_req_3,
+  s_n_llhttp__internal__n_start_req_8,
+  s_n_llhttp__internal__n_start_req_9,
+  s_n_llhttp__internal__n_start_req_10,
+  s_n_llhttp__internal__n_start_req_12,
+  s_n_llhttp__internal__n_start_req_13,
+  s_n_llhttp__internal__n_start_req_11,
+  s_n_llhttp__internal__n_start_req_15,
+  s_n_llhttp__internal__n_start_req_16,
+  s_n_llhttp__internal__n_start_req_18,
+  s_n_llhttp__internal__n_start_req_20,
+  s_n_llhttp__internal__n_start_req_21,
+  s_n_llhttp__internal__n_start_req_19,
+  s_n_llhttp__internal__n_start_req_17,
+  s_n_llhttp__internal__n_start_req_22,
+  s_n_llhttp__internal__n_start_req_14,
+  s_n_llhttp__internal__n_start_req_23,
+  s_n_llhttp__internal__n_start_req_24,
+  s_n_llhttp__internal__n_start_req_26,
+  s_n_llhttp__internal__n_start_req_27,
+  s_n_llhttp__internal__n_start_req_30,
+  s_n_llhttp__internal__n_start_req_31,
+  s_n_llhttp__internal__n_start_req_29,
+  s_n_llhttp__internal__n_start_req_28,
+  s_n_llhttp__internal__n_start_req_33,
+  s_n_llhttp__internal__n_start_req_32,
+  s_n_llhttp__internal__n_start_req_25,
+  s_n_llhttp__internal__n_start_req_36,
+  s_n_llhttp__internal__n_start_req_37,
+  s_n_llhttp__internal__n_start_req_35,
+  s_n_llhttp__internal__n_start_req_34,
+  s_n_llhttp__internal__n_start_req_39,
+  s_n_llhttp__internal__n_start_req_40,
+  s_n_llhttp__internal__n_start_req_41,
+  s_n_llhttp__internal__n_start_req_38,
+  s_n_llhttp__internal__n_start_req_42,
+  s_n_llhttp__internal__n_start_req_45,
+  s_n_llhttp__internal__n_start_req_47,
+  s_n_llhttp__internal__n_start_req_48,
+  s_n_llhttp__internal__n_start_req_46,
+  s_n_llhttp__internal__n_start_req_49,
+  s_n_llhttp__internal__n_start_req_44,
+  s_n_llhttp__internal__n_start_req_43,
+  s_n_llhttp__internal__n_start_req,
+  s_n_llhttp__internal__n_res_line_almost_done,
+  s_n_llhttp__internal__n_res_status,
+  s_n_llhttp__internal__n_span_start_llhttp__on_status,
+  s_n_llhttp__internal__n_res_status_start,
+  s_n_llhttp__internal__n_res_status_code_otherwise,
+  s_n_llhttp__internal__n_res_status_code,
+  s_n_llhttp__internal__n_res_http_end,
+  s_n_llhttp__internal__n_res_http_minor,
+  s_n_llhttp__internal__n_res_http_dot,
+  s_n_llhttp__internal__n_res_http_major,
+  s_n_llhttp__internal__n_start_res,
+  s_n_llhttp__internal__n_req_or_res_method_2,
+  s_n_llhttp__internal__n_req_or_res_method_3,
+  s_n_llhttp__internal__n_req_or_res_method_1,
+  s_n_llhttp__internal__n_req_or_res_method,
+  s_n_llhttp__internal__n_start_req_or_res,
+  s_n_llhttp__internal__n_invoke_load_type,
+  s_n_llhttp__internal__n_start,
+};
+typedef enum llparse_state_e llparse_state_t;
+
+int llhttp__on_url(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__on_header_field(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__on_header_value(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__on_body(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__on_status(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__internal__c_update_finish(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->finish = 2;
+  return 0;
+}
+
+int llhttp__on_message_begin(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__internal__c_load_type(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return state->type;
+}
+
+int llhttp__internal__c_store_method(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp,
+    int match) {
+  state->method = match;
+  return 0;
+}
+
+int llhttp__internal__c_is_equal_method(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return state->method == 5;
+}
+
+int llhttp__internal__c_update_http_major(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->http_major = 0;
+  return 0;
+}
+
+int llhttp__internal__c_update_http_minor(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->http_minor = 9;
+  return 0;
+}
+
+int llhttp__internal__c_test_flags(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return (state->flags & 128) == 128;
+}
+
+int llhttp__on_chunk_complete(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__on_message_complete(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_upgrade(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return state->upgrade == 1;
+}
+
+int llhttp__after_message_complete(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__internal__c_test_flags_1(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return (state->flags & 40) == 40;
+}
+
+int llhttp__before_headers_complete(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__on_headers_complete(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__after_headers_complete(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__internal__c_update_content_length(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->content_length = 0;
+  return 0;
+}
+
+int llhttp__internal__c_mul_add_content_length(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp,
+    int match) {
+  /* Multiplication overflow */
+  if (state->content_length > 0xffffffffffffffffULL / 16) {
+    return 1;
+  }
+  
+  state->content_length *= 16;
+  
+  /* Addition overflow */
+  if (match >= 0) {
+    if (state->content_length > 0xffffffffffffffffULL - match) {
+      return 1;
+    }
+  } else {
+    if (state->content_length < 0ULL - match) {
+      return 1;
+    }
+  }
+  state->content_length += match;
+  return 0;
+}
+
+int llhttp__on_chunk_header(
+    llhttp__internal_t* s, const unsigned char* p,
+    const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_content_length(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return state->content_length == 0;
+}
+
+int llhttp__internal__c_or_flags(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 128;
+  return 0;
+}
+
+int llhttp__internal__c_update_finish_1(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->finish = 1;
+  return 0;
+}
+
+int llhttp__internal__c_or_flags_1(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 64;
+  return 0;
+}
+
+int llhttp__internal__c_update_upgrade(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->upgrade = 1;
+  return 0;
+}
+
+int llhttp__internal__c_store_header_state(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp,
+    int match) {
+  state->header_state = match;
+  return 0;
+}
+
+int llhttp__internal__c_load_header_state(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return state->header_state;
+}
+
+int llhttp__internal__c_or_flags_3(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 1;
+  return 0;
+}
+
+int llhttp__internal__c_update_header_state(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->header_state = 1;
+  return 0;
+}
+
+int llhttp__internal__c_or_flags_4(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 2;
+  return 0;
+}
+
+int llhttp__internal__c_or_flags_5(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 4;
+  return 0;
+}
+
+int llhttp__internal__c_or_flags_6(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 8;
+  return 0;
+}
+
+int llhttp__internal__c_update_header_state_2(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->header_state = 6;
+  return 0;
+}
+
+int llhttp__internal__c_update_header_state_4(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->header_state = 0;
+  return 0;
+}
+
+int llhttp__internal__c_update_header_state_5(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->header_state = 5;
+  return 0;
+}
+
+int llhttp__internal__c_update_header_state_6(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->header_state = 7;
+  return 0;
+}
+
+int llhttp__internal__c_test_flags_2(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return (state->flags & 32) == 32;
+}
+
+int llhttp__internal__c_mul_add_content_length_1(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp,
+    int match) {
+  /* Multiplication overflow */
+  if (state->content_length > 0xffffffffffffffffULL / 10) {
+    return 1;
+  }
+  
+  state->content_length *= 10;
+  
+  /* Addition overflow */
+  if (match >= 0) {
+    if (state->content_length > 0xffffffffffffffffULL - match) {
+      return 1;
+    }
+  } else {
+    if (state->content_length < 0ULL - match) {
+      return 1;
+    }
+  }
+  state->content_length += match;
+  return 0;
+}
+
+int llhttp__internal__c_or_flags_15(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 32;
+  return 0;
+}
+
+int llhttp__internal__c_update_header_state_8(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->header_state = 8;
+  return 0;
+}
+
+int llhttp__internal__c_or_flags_16(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->flags |= 16;
+  return 0;
+}
+
+int llhttp__internal__c_store_http_major(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp,
+    int match) {
+  state->http_major = match;
+  return 0;
+}
+
+int llhttp__internal__c_store_http_minor(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp,
+    int match) {
+  state->http_minor = match;
+  return 0;
+}
+
+int llhttp__internal__c_is_equal_method_1(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  return state->method == 33;
+}
+
+int llhttp__internal__c_update_status_code(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->status_code = 0;
+  return 0;
+}
+
+int llhttp__internal__c_mul_add_status_code(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp,
+    int match) {
+  /* Multiplication overflow */
+  if (state->status_code > 0xffff / 10) {
+    return 1;
+  }
+  
+  state->status_code *= 10;
+  
+  /* Addition overflow */
+  if (match >= 0) {
+    if (state->status_code > 0xffff - match) {
+      return 1;
+    }
+  } else {
+    if (state->status_code < 0 - match) {
+      return 1;
+    }
+  }
+  state->status_code += match;
+  
+  /* Enforce maximum */
+  if (state->status_code > 999) {
+    return 1;
+  }
+  return 0;
+}
+
+int llhttp__internal__c_update_type(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->type = 1;
+  return 0;
+}
+
+int llhttp__internal__c_update_type_1(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  state->type = 2;
+  return 0;
+}
+
+int llhttp__internal_init(llhttp__internal_t* state) {
+  memset(state, 0, sizeof(*state));
+  state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start;
+  return 0;
+}
+
+static llparse_state_t llhttp__internal__run(
+    llhttp__internal_t* state,
+    const unsigned char* p,
+    const unsigned char* endp) {
+  int match;
+  switch ((llparse_state_t) (intptr_t) state->_current) {
+    case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete:
+    s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: {
+      switch (llhttp__after_message_complete(state, p, endp)) {
+        default:
+          goto s_n_llhttp__internal__n_start;
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_pause_1:
+    s_n_llhttp__internal__n_pause_1: {
+      state->error = 0x15;
+      state->reason = "Pause on CONNECT/Upgrade";
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+      return s_error;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_invoke_is_equal_upgrade:
+    s_n_llhttp__internal__n_invoke_is_equal_upgrade: {
+      switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) {
+        case 0:
+          goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+        default:
+          goto s_n_llhttp__internal__n_pause_1;
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2:
+    s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: {
+      switch (llhttp__on_message_complete(state, p, endp)) {
+        case 0:
+          goto s_n_llhttp__internal__n_invoke_is_equal_upgrade;
+        case 20:
+          goto s_n_llhttp__internal__n_pause_5;
+        default:
+          goto s_n_llhttp__internal__n_error_9;
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_chunk_data_almost_done_skip:
+    s_n_llhttp__internal__n_chunk_data_almost_done_skip: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_chunk_data_almost_done_skip;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_chunk_data_almost_done:
+    s_n_llhttp__internal__n_chunk_data_almost_done: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_chunk_data_almost_done;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_chunk_data_almost_done_skip;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_consume_content_length:
+    s_n_llhttp__internal__n_consume_content_length: {
+      size_t avail;
+      size_t need;
+      
+      avail = endp - p;
+      need = state->content_length;
+      if (avail >= need) {
+        p += need;
+        state->content_length = 0;
+        goto s_n_llhttp__internal__n_span_end_llhttp__on_body;
+      }
+      
+      state->content_length -= avail;
+      return s_n_llhttp__internal__n_consume_content_length;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_body:
+    s_n_llhttp__internal__n_span_start_llhttp__on_body: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_body;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_body;
+      goto s_n_llhttp__internal__n_consume_content_length;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_invoke_is_equal_content_length:
+    s_n_llhttp__internal__n_invoke_is_equal_content_length: {
+      switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) {
+        case 0:
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_body;
+        default:
+          goto s_n_llhttp__internal__n_invoke_or_flags;
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_chunk_size_almost_done:
+    s_n_llhttp__internal__n_chunk_size_almost_done: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_chunk_size_almost_done;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_chunk_parameters:
+    s_n_llhttp__internal__n_chunk_parameters: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_chunk_parameters;
+      }
+      switch (*p) {
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_chunk_size_almost_done;
+        }
+        default: {
+          p++;
+          goto s_n_llhttp__internal__n_chunk_parameters;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_chunk_size_otherwise:
+    s_n_llhttp__internal__n_chunk_size_otherwise: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_chunk_size_otherwise;
+      }
+      switch (*p) {
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_chunk_size_almost_done;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_chunk_parameters;
+        }
+        case ';': {
+          p++;
+          goto s_n_llhttp__internal__n_chunk_parameters;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_6;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_chunk_size:
+    s_n_llhttp__internal__n_chunk_size: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_chunk_size;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'A': {
+          p++;
+          match = 10;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'B': {
+          p++;
+          match = 11;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'C': {
+          p++;
+          match = 12;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'D': {
+          p++;
+          match = 13;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'E': {
+          p++;
+          match = 14;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'F': {
+          p++;
+          match = 15;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'a': {
+          p++;
+          match = 10;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'b': {
+          p++;
+          match = 11;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'c': {
+          p++;
+          match = 12;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'd': {
+          p++;
+          match = 13;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'e': {
+          p++;
+          match = 14;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'f': {
+          p++;
+          match = 15;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_chunk_size_otherwise;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_chunk_size_digit:
+    s_n_llhttp__internal__n_chunk_size_digit: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_chunk_size_digit;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'A': {
+          p++;
+          match = 10;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'B': {
+          p++;
+          match = 11;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'C': {
+          p++;
+          match = 12;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'D': {
+          p++;
+          match = 13;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'E': {
+          p++;
+          match = 14;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'F': {
+          p++;
+          match = 15;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'a': {
+          p++;
+          match = 10;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'b': {
+          p++;
+          match = 11;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'c': {
+          p++;
+          match = 12;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'd': {
+          p++;
+          match = 13;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'e': {
+          p++;
+          match = 14;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        case 'f': {
+          p++;
+          match = 15;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_8;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_invoke_update_content_length:
+    s_n_llhttp__internal__n_invoke_update_content_length: {
+      switch (llhttp__internal__c_update_content_length(state, p, endp)) {
+        default:
+          goto s_n_llhttp__internal__n_chunk_size_digit;
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_consume_content_length_1:
+    s_n_llhttp__internal__n_consume_content_length_1: {
+      size_t avail;
+      size_t need;
+      
+      avail = endp - p;
+      need = state->content_length;
+      if (avail >= need) {
+        p += need;
+        state->content_length = 0;
+        goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1;
+      }
+      
+      state->content_length -= avail;
+      return s_n_llhttp__internal__n_consume_content_length_1;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_body_1:
+    s_n_llhttp__internal__n_span_start_llhttp__on_body_1: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_body_1;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_body;
+      goto s_n_llhttp__internal__n_consume_content_length_1;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_eof:
+    s_n_llhttp__internal__n_eof: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_eof;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_eof;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_body_2:
+    s_n_llhttp__internal__n_span_start_llhttp__on_body_2: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_body_2;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_body;
+      goto s_n_llhttp__internal__n_invoke_update_finish_1;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete:
+    s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: {
+      switch (llhttp__after_headers_complete(state, p, endp)) {
+        case 1:
+          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1;
+        case 2:
+          goto s_n_llhttp__internal__n_invoke_update_content_length;
+        case 3:
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1;
+        case 4:
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2;
+        default:
+          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete;
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_headers_almost_done:
+    s_n_llhttp__internal__n_headers_almost_done: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_headers_almost_done;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_invoke_test_flags;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_header_value:
+    s_n_llhttp__internal__n_span_start_llhttp__on_header_value: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_header_value;
+      goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_discard_lws:
+    s_n_llhttp__internal__n_header_value_discard_lws: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_discard_lws;
+      }
+      switch (*p) {
+        case 9: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_discard_ws;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_discard_ws;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_load_header_state;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_discard_ws_almost_done:
+    s_n_llhttp__internal__n_header_value_discard_ws_almost_done: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_discard_ws_almost_done;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_header_value_discard_lws;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_lws:
+    s_n_llhttp__internal__n_header_value_lws: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_lws;
+      }
+      switch (*p) {
+        case 9: {
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+        }
+        case ' ': {
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_load_header_state_2;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_almost_done:
+    s_n_llhttp__internal__n_header_value_almost_done: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_almost_done;
+      }
+      switch (*p) {
+        case 10: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_lws;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_12;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_otherwise:
+    s_n_llhttp__internal__n_header_value_otherwise: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_otherwise;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_13;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_connection_token:
+    s_n_llhttp__internal__n_header_value_connection_token: {
+      static uint8_t lookup_table[] = {
+        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,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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
+      };
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_connection_token;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection_token;
+        }
+        case 2: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_header_value_otherwise;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_connection_ws:
+    s_n_llhttp__internal__n_header_value_connection_ws: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_connection_ws;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_header_value_otherwise;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_header_value_otherwise;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection_ws;
+        }
+        case ',': {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_load_header_state_3;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_4;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_connection_1:
+    s_n_llhttp__internal__n_header_value_connection_1: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_connection_1;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob3, 4);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_update_header_state_2;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_value_connection_1;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_header_value_connection_token;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_connection_2:
+    s_n_llhttp__internal__n_header_value_connection_2: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_connection_2;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob4, 9);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_update_header_state_5;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_value_connection_2;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_header_value_connection_token;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_connection_3:
+    s_n_llhttp__internal__n_header_value_connection_3: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_connection_3;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_update_header_state_6;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_value_connection_3;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_header_value_connection_token;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_connection:
+    s_n_llhttp__internal__n_header_value_connection: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_connection;
+      }
+      switch (((*p) | 0x20)) {
+        case 9: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection;
+        }
+        case 'c': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection_1;
+        }
+        case 'k': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection_2;
+        }
+        case 'u': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_connection_3;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_header_value_connection_token;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_error_15:
+    s_n_llhttp__internal__n_error_15: {
+      state->error = 0xb;
+      state->reason = "Content-Length overflow";
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_error;
+      return s_error;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value:
+    s_n_llhttp__internal__n_header_value: {
+      static uint8_t lookup_table[] = {
+        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,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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
+      };
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_header_value_otherwise;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_discard_rws:
+    s_n_llhttp__internal__n_header_value_discard_rws: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_discard_rws;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_header_value_otherwise;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_header_value_otherwise;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_discard_rws;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_7;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_error_16:
+    s_n_llhttp__internal__n_error_16: {
+      state->error = 0xb;
+      state->reason = "Invalid character in Content-Length";
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_error;
+      return s_error;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_content_length_ws:
+    s_n_llhttp__internal__n_header_value_content_length_ws: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_content_length_ws;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_invoke_or_flags_15;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_invoke_or_flags_15;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_content_length_ws;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_content_length:
+    s_n_llhttp__internal__n_header_value_content_length: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_content_length;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_header_value_content_length_ws;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_te_chunked_1:
+    s_n_llhttp__internal__n_header_value_te_chunked_1: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_te_chunked_1;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_update_header_state_8;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_value_te_chunked_1;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_7;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_te_chunked:
+    s_n_llhttp__internal__n_header_value_te_chunked: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_te_chunked;
+      }
+      switch (((*p) | 0x20)) {
+        case 10: {
+          goto s_n_llhttp__internal__n_header_value_discard_rws;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_header_value_discard_rws;
+        }
+        case ' ': {
+          goto s_n_llhttp__internal__n_header_value_discard_rws;
+        }
+        case 'c': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_te_chunked_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_7;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1:
+    s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_header_value;
+      goto s_n_llhttp__internal__n_invoke_load_header_state_1;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_value_discard_ws:
+    s_n_llhttp__internal__n_header_value_discard_ws: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_value_discard_ws;
+      }
+      switch (*p) {
+        case 9: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_discard_ws;
+        }
+        case 10: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_discard_lws;
+        }
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_header_value_discard_ws;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_general_otherwise:
+    s_n_llhttp__internal__n_header_field_general_otherwise: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_general_otherwise;
+      }
+      switch (*p) {
+        case ':': {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_17;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_general:
+    s_n_llhttp__internal__n_header_field_general: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+        0, 1, 1, 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, 1, 1,
+        1, 1, 1, 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, 0, 1, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+      };
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_general;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_general;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_header_field_general_otherwise;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_colon:
+    s_n_llhttp__internal__n_header_field_colon: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_colon;
+      }
+      switch (*p) {
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_colon;
+        }
+        case ':': {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_9;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_3:
+    s_n_llhttp__internal__n_header_field_3: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_3;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob2, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_store_header_state;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_field_3;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_4:
+    s_n_llhttp__internal__n_header_field_4: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_4;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob7, 10);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_store_header_state;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_field_4;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_2:
+    s_n_llhttp__internal__n_header_field_2: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_2;
+      }
+      switch (((*p) | 0x20)) {
+        case 'n': {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_3;
+        }
+        case 't': {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_4;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_1:
+    s_n_llhttp__internal__n_header_field_1: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_1;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob1, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_2;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_field_1;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_5:
+    s_n_llhttp__internal__n_header_field_5: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_5;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob8, 15);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_store_header_state;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_field_5;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_6:
+    s_n_llhttp__internal__n_header_field_6: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_6;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob9, 16);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_store_header_state;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_field_6;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_7:
+    s_n_llhttp__internal__n_header_field_7: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_7;
+      }
+      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob10, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_store_header_state;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_header_field_7;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field:
+    s_n_llhttp__internal__n_header_field: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field;
+      }
+      switch (((*p) | 0x20)) {
+        case 'c': {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_1;
+        }
+        case 'p': {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_5;
+        }
+        case 't': {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_6;
+        }
+        case 'u': {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_7;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_header_field:
+    s_n_llhttp__internal__n_span_start_llhttp__on_header_field: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_header_field;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_header_field;
+      goto s_n_llhttp__internal__n_header_field;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_header_field_start:
+    s_n_llhttp__internal__n_header_field_start: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_header_field_start;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_headers_almost_done;
+        }
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_headers_almost_done;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_skip_to_http09:
+    s_n_llhttp__internal__n_url_skip_to_http09: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_skip_to_http09;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_invoke_update_http_major;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_skip_lf_to_http09:
+    s_n_llhttp__internal__n_url_skip_lf_to_http09: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_skip_lf_to_http09;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob11, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_update_http_major;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_url_skip_lf_to_http09;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_18;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_end_1:
+    s_n_llhttp__internal__n_req_http_end_1: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_end_1;
+      }
+      switch (*p) {
+        case 10: {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_start;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_19;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_end:
+    s_n_llhttp__internal__n_req_http_end: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_end;
+      }
+      switch (*p) {
+        case 10: {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_start;
+        }
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_req_http_end_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_19;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_minor:
+    s_n_llhttp__internal__n_req_http_minor: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_minor;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_20;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_dot:
+    s_n_llhttp__internal__n_req_http_dot: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_dot;
+      }
+      switch (*p) {
+        case '.': {
+          p++;
+          goto s_n_llhttp__internal__n_req_http_minor;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_21;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_major:
+    s_n_llhttp__internal__n_req_http_major: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_major;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_store_http_major;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_22;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_start_1:
+    s_n_llhttp__internal__n_req_http_start_1: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_start_1;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob12, 4);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_req_http_major;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_req_http_start_1;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_24;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_start_2:
+    s_n_llhttp__internal__n_req_http_start_2: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_start_2;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_is_equal_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_req_http_start_2;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_24;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_http_start:
+    s_n_llhttp__internal__n_req_http_start: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_http_start;
+      }
+      switch (*p) {
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_req_http_start;
+        }
+        case 'H': {
+          p++;
+          goto s_n_llhttp__internal__n_req_http_start_1;
+        }
+        case 'I': {
+          p++;
+          goto s_n_llhttp__internal__n_req_http_start_2;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_24;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_skip_to_http:
+    s_n_llhttp__internal__n_url_skip_to_http: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_skip_to_http;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_req_http_start;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_fragment:
+    s_n_llhttp__internal__n_url_fragment: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 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 (p == endp) {
+        return s_n_llhttp__internal__n_url_fragment;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_url_fragment;
+        }
+        case 2: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6;
+        }
+        case 3: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7;
+        }
+        case 4: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_25;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_end_stub_query_3:
+    s_n_llhttp__internal__n_span_end_stub_query_3: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_end_stub_query_3;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_url_fragment;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_query:
+    s_n_llhttp__internal__n_url_query: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        4, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 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 (p == endp) {
+        return s_n_llhttp__internal__n_url_query;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_url_query;
+        }
+        case 2: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9;
+        }
+        case 3: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10;
+        }
+        case 4: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11;
+        }
+        case 5: {
+          goto s_n_llhttp__internal__n_span_end_stub_query_3;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_26;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_query_or_fragment:
+    s_n_llhttp__internal__n_url_query_or_fragment: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_query_or_fragment;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4;
+        }
+        case ' ': {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5;
+        }
+        case '#': {
+          p++;
+          goto s_n_llhttp__internal__n_url_fragment;
+        }
+        case '?': {
+          p++;
+          goto s_n_llhttp__internal__n_url_query;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_27;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_path:
+    s_n_llhttp__internal__n_url_path: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 1, 1, 0, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 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 (p == endp) {
+        return s_n_llhttp__internal__n_url_path;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_url_path;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_url_query_or_fragment;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_stub_path_2:
+    s_n_llhttp__internal__n_span_start_stub_path_2: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_stub_path_2;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_url_path;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_stub_path:
+    s_n_llhttp__internal__n_span_start_stub_path: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_stub_path;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_url_path;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_stub_path_1:
+    s_n_llhttp__internal__n_span_start_stub_path_1: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_stub_path_1;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_url_path;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_server_with_at:
+    s_n_llhttp__internal__n_url_server_with_at: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
+        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6,
+        7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4,
+        0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+      };
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_server_with_at;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12;
+        }
+        case 2: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13;
+        }
+        case 3: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14;
+        }
+        case 4: {
+          p++;
+          goto s_n_llhttp__internal__n_url_server;
+        }
+        case 5: {
+          goto s_n_llhttp__internal__n_span_start_stub_path_1;
+        }
+        case 6: {
+          p++;
+          goto s_n_llhttp__internal__n_url_query;
+        }
+        case 7: {
+          p++;
+          goto s_n_llhttp__internal__n_error_28;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_29;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_server:
+    s_n_llhttp__internal__n_url_server: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
+        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6,
+        7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4,
+        0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+      };
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_server;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url;
+        }
+        case 2: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1;
+        }
+        case 3: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2;
+        }
+        case 4: {
+          p++;
+          goto s_n_llhttp__internal__n_url_server;
+        }
+        case 5: {
+          goto s_n_llhttp__internal__n_span_start_stub_path;
+        }
+        case 6: {
+          p++;
+          goto s_n_llhttp__internal__n_url_query;
+        }
+        case 7: {
+          p++;
+          goto s_n_llhttp__internal__n_url_server_with_at;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_30;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_schema_delim_1:
+    s_n_llhttp__internal__n_url_schema_delim_1: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_schema_delim_1;
+      }
+      switch (*p) {
+        case '/': {
+          p++;
+          goto s_n_llhttp__internal__n_url_server;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_32;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_schema_delim:
+    s_n_llhttp__internal__n_url_schema_delim: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_schema_delim;
+      }
+      switch (*p) {
+        case 10: {
+          p++;
+          goto s_n_llhttp__internal__n_error_31;
+        }
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_error_31;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_error_31;
+        }
+        case '/': {
+          p++;
+          goto s_n_llhttp__internal__n_url_schema_delim_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_32;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_end_stub_schema:
+    s_n_llhttp__internal__n_span_end_stub_schema: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_end_stub_schema;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_url_schema_delim;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_schema:
+    s_n_llhttp__internal__n_url_schema: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 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, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 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, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+      };
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_schema;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_error_31;
+        }
+        case 2: {
+          goto s_n_llhttp__internal__n_span_end_stub_schema;
+        }
+        case 3: {
+          p++;
+          goto s_n_llhttp__internal__n_url_schema;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_33;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_url_start:
+    s_n_llhttp__internal__n_url_start: {
+      static uint8_t lookup_table[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 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, 2, 0, 0, 0, 0, 2,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 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, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+      };
+      if (p == endp) {
+        return s_n_llhttp__internal__n_url_start;
+      }
+      switch (lookup_table[(uint8_t) *p]) {
+        case 1: {
+          p++;
+          goto s_n_llhttp__internal__n_error_31;
+        }
+        case 2: {
+          goto s_n_llhttp__internal__n_span_start_stub_path_2;
+        }
+        case 3: {
+          goto s_n_llhttp__internal__n_url_schema;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_34;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_url_1:
+    s_n_llhttp__internal__n_span_start_llhttp__on_url_1: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_url_1;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_url;
+      goto s_n_llhttp__internal__n_url_start;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_url:
+    s_n_llhttp__internal__n_span_start_llhttp__on_url: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_url;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_url;
+      goto s_n_llhttp__internal__n_url_server;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_spaces_before_url:
+    s_n_llhttp__internal__n_req_spaces_before_url: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_spaces_before_url;
+      }
+      switch (*p) {
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_req_spaces_before_url;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_is_equal_method;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_first_space_before_url:
+    s_n_llhttp__internal__n_req_first_space_before_url: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_first_space_before_url;
+      }
+      switch (*p) {
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_req_spaces_before_url;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_35;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_1:
+    s_n_llhttp__internal__n_start_req_1: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_1;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob0, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 19;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_1;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_2:
+    s_n_llhttp__internal__n_start_req_2: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_2;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 16;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_2;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_4:
+    s_n_llhttp__internal__n_start_req_4: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_4;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 22;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_4;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_6:
+    s_n_llhttp__internal__n_start_req_6: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_6;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_6;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_7:
+    s_n_llhttp__internal__n_start_req_7: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_7;
+      }
+      switch (*p) {
+        case 'Y': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_5:
+    s_n_llhttp__internal__n_start_req_5: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_5;
+      }
+      switch (*p) {
+        case 'N': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_6;
+        }
+        case 'P': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_7;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_3:
+    s_n_llhttp__internal__n_start_req_3: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_3;
+      }
+      switch (*p) {
+        case 'H': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_4;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_5;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_8:
+    s_n_llhttp__internal__n_start_req_8: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_8;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 5);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_8;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_9:
+    s_n_llhttp__internal__n_start_req_9: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_9;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_9;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_10:
+    s_n_llhttp__internal__n_start_req_10: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_10;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_10;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_12:
+    s_n_llhttp__internal__n_start_req_12: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_12;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 31;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_12;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_13:
+    s_n_llhttp__internal__n_start_req_13: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_13;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_13;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_11:
+    s_n_llhttp__internal__n_start_req_11: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_11;
+      }
+      switch (*p) {
+        case 'I': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_12;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_13;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_15:
+    s_n_llhttp__internal__n_start_req_15: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_15;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 24;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_15;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_16:
+    s_n_llhttp__internal__n_start_req_16: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_16;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 23;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_16;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_18:
+    s_n_llhttp__internal__n_start_req_18: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_18;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 7);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 21;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_18;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_20:
+    s_n_llhttp__internal__n_start_req_20: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_20;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 30;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_20;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_21:
+    s_n_llhttp__internal__n_start_req_21: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_21;
+      }
+      switch (*p) {
+        case 'L': {
+          p++;
+          match = 10;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_19:
+    s_n_llhttp__internal__n_start_req_19: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_19;
+      }
+      switch (*p) {
+        case 'A': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_20;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_21;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_17:
+    s_n_llhttp__internal__n_start_req_17: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_17;
+      }
+      switch (*p) {
+        case 'A': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_18;
+        }
+        case 'C': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_19;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_22:
+    s_n_llhttp__internal__n_start_req_22: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_22;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 11;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_22;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_14:
+    s_n_llhttp__internal__n_start_req_14: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_14;
+      }
+      switch (*p) {
+        case '-': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_15;
+        }
+        case 'E': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_16;
+        }
+        case 'K': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_17;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_22;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_23:
+    s_n_llhttp__internal__n_start_req_23: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_23;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 5);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 25;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_23;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_24:
+    s_n_llhttp__internal__n_start_req_24: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_24;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 6);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_24;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_26:
+    s_n_llhttp__internal__n_start_req_26: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_26;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 28;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_26;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_27:
+    s_n_llhttp__internal__n_start_req_27: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_27;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_27;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_30:
+    s_n_llhttp__internal__n_start_req_30: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_30;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 12;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_30;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_31:
+    s_n_llhttp__internal__n_start_req_31: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_31;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 4);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 13;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_31;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_29:
+    s_n_llhttp__internal__n_start_req_29: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_29;
+      }
+      switch (*p) {
+        case 'F': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_30;
+        }
+        case 'P': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_31;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_28:
+    s_n_llhttp__internal__n_start_req_28: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_28;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_29;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_28;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_33:
+    s_n_llhttp__internal__n_start_req_33: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_33;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 29;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_33;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_32:
+    s_n_llhttp__internal__n_start_req_32: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_32;
+      }
+      switch (*p) {
+        case 'R': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_33;
+        }
+        case 'T': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_25:
+    s_n_llhttp__internal__n_start_req_25: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_25;
+      }
+      switch (*p) {
+        case 'A': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_26;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_27;
+        }
+        case 'R': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_28;
+        }
+        case 'U': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_32;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_36:
+    s_n_llhttp__internal__n_start_req_36: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_36;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 17;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_36;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_37:
+    s_n_llhttp__internal__n_start_req_37: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_37;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 20;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_37;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_35:
+    s_n_llhttp__internal__n_start_req_35: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_35;
+      }
+      switch (*p) {
+        case 'B': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_36;
+        }
+        case 'P': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_37;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_34:
+    s_n_llhttp__internal__n_start_req_34: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_34;
+      }
+      switch (*p) {
+        case 'E': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_35;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_39:
+    s_n_llhttp__internal__n_start_req_39: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_39;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 4);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 14;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_39;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_40:
+    s_n_llhttp__internal__n_start_req_40: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_40;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 4);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 33;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_40;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_41:
+    s_n_llhttp__internal__n_start_req_41: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_41;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 7);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 26;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_41;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_38:
+    s_n_llhttp__internal__n_start_req_38: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_38;
+      }
+      switch (*p) {
+        case 'E': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_39;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_40;
+        }
+        case 'U': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_41;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_42:
+    s_n_llhttp__internal__n_start_req_42: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_42;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 4);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_42;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_45:
+    s_n_llhttp__internal__n_start_req_45: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_45;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 18;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_45;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_47:
+    s_n_llhttp__internal__n_start_req_47: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_47;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 32;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_47;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_48:
+    s_n_llhttp__internal__n_start_req_48: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_48;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 15;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_48;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_46:
+    s_n_llhttp__internal__n_start_req_46: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_46;
+      }
+      switch (*p) {
+        case 'I': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_47;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_48;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_49:
+    s_n_llhttp__internal__n_start_req_49: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_49;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 8);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 27;
+          goto s_n_llhttp__internal__n_invoke_store_method_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_req_49;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_44:
+    s_n_llhttp__internal__n_start_req_44: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_44;
+      }
+      switch (*p) {
+        case 'B': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_45;
+        }
+        case 'L': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_46;
+        }
+        case 'S': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_49;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_43:
+    s_n_llhttp__internal__n_start_req_43: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_43;
+      }
+      switch (*p) {
+        case 'N': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_44;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req:
+    s_n_llhttp__internal__n_start_req: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req;
+      }
+      switch (*p) {
+        case 'A': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_1;
+        }
+        case 'B': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_2;
+        }
+        case 'C': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_3;
+        }
+        case 'D': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_8;
+        }
+        case 'G': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_9;
+        }
+        case 'H': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_10;
+        }
+        case 'L': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_11;
+        }
+        case 'M': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_14;
+        }
+        case 'N': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_23;
+        }
+        case 'O': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_24;
+        }
+        case 'P': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_25;
+        }
+        case 'R': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_34;
+        }
+        case 'S': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_38;
+        }
+        case 'T': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_42;
+        }
+        case 'U': {
+          p++;
+          goto s_n_llhttp__internal__n_start_req_43;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_43;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_line_almost_done:
+    s_n_llhttp__internal__n_res_line_almost_done: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_line_almost_done;
+      }
+      p++;
+      goto s_n_llhttp__internal__n_header_field_start;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_status:
+    s_n_llhttp__internal__n_res_status: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_status;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_status;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1;
+        }
+        default: {
+          p++;
+          goto s_n_llhttp__internal__n_res_status;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_span_start_llhttp__on_status:
+    s_n_llhttp__internal__n_span_start_llhttp__on_status: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_span_start_llhttp__on_status;
+      }
+      state->_span_pos0 = (void*) p;
+      state->_span_cb0 = llhttp__on_status;
+      goto s_n_llhttp__internal__n_res_status;
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_status_start:
+    s_n_llhttp__internal__n_res_status_start: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_status_start;
+      }
+      switch (*p) {
+        case 10: {
+          p++;
+          goto s_n_llhttp__internal__n_header_field_start;
+        }
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_res_line_almost_done;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_span_start_llhttp__on_status;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_status_code_otherwise:
+    s_n_llhttp__internal__n_res_status_code_otherwise: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_status_code_otherwise;
+      }
+      switch (*p) {
+        case 10: {
+          goto s_n_llhttp__internal__n_res_status_start;
+        }
+        case 13: {
+          goto s_n_llhttp__internal__n_res_status_start;
+        }
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_res_status_start;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_37;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_status_code:
+    s_n_llhttp__internal__n_res_status_code: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_status_code;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_res_status_code_otherwise;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_http_end:
+    s_n_llhttp__internal__n_res_http_end: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_http_end;
+      }
+      switch (*p) {
+        case ' ': {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_update_status_code;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_38;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_http_minor:
+    s_n_llhttp__internal__n_res_http_minor: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_http_minor;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_39;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_http_dot:
+    s_n_llhttp__internal__n_res_http_dot: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_http_dot;
+      }
+      switch (*p) {
+        case '.': {
+          p++;
+          goto s_n_llhttp__internal__n_res_http_minor;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_40;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_res_http_major:
+    s_n_llhttp__internal__n_res_http_major: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_res_http_major;
+      }
+      switch (*p) {
+        case '0': {
+          p++;
+          match = 0;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '1': {
+          p++;
+          match = 1;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '2': {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '3': {
+          p++;
+          match = 3;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '4': {
+          p++;
+          match = 4;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '5': {
+          p++;
+          match = 5;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '6': {
+          p++;
+          match = 6;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '7': {
+          p++;
+          match = 7;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '8': {
+          p++;
+          match = 8;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        case '9': {
+          p++;
+          match = 9;
+          goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_41;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_res:
+    s_n_llhttp__internal__n_start_res: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_res;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 5);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_res_http_major;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_start_res;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_44;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_or_res_method_2:
+    s_n_llhttp__internal__n_req_or_res_method_2: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_or_res_method_2;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 2);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          match = 2;
+          goto s_n_llhttp__internal__n_invoke_store_method;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_req_or_res_method_2;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_42;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_or_res_method_3:
+    s_n_llhttp__internal__n_req_or_res_method_3: {
+      llparse_match_t match_seq;
+      
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_or_res_method_3;
+      }
+      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3);
+      p = match_seq.current;
+      switch (match_seq.status) {
+        case kMatchComplete: {
+          p++;
+          goto s_n_llhttp__internal__n_invoke_update_type_1;
+        }
+        case kMatchPause: {
+          return s_n_llhttp__internal__n_req_or_res_method_3;
+        }
+        case kMatchMismatch: {
+          goto s_n_llhttp__internal__n_error_42;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_or_res_method_1:
+    s_n_llhttp__internal__n_req_or_res_method_1: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_or_res_method_1;
+      }
+      switch (*p) {
+        case 'E': {
+          p++;
+          goto s_n_llhttp__internal__n_req_or_res_method_2;
+        }
+        case 'T': {
+          p++;
+          goto s_n_llhttp__internal__n_req_or_res_method_3;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_42;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_req_or_res_method:
+    s_n_llhttp__internal__n_req_or_res_method: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_req_or_res_method;
+      }
+      switch (*p) {
+        case 'H': {
+          p++;
+          goto s_n_llhttp__internal__n_req_or_res_method_1;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_error_42;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start_req_or_res:
+    s_n_llhttp__internal__n_start_req_or_res: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start_req_or_res;
+      }
+      switch (*p) {
+        case 'H': {
+          goto s_n_llhttp__internal__n_req_or_res_method;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_type_2;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_invoke_load_type:
+    s_n_llhttp__internal__n_invoke_load_type: {
+      switch (llhttp__internal__c_load_type(state, p, endp)) {
+        case 1:
+          goto s_n_llhttp__internal__n_start_req;
+        case 2:
+          goto s_n_llhttp__internal__n_start_res;
+        default:
+          goto s_n_llhttp__internal__n_start_req_or_res;
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    case s_n_llhttp__internal__n_start:
+    s_n_llhttp__internal__n_start: {
+      if (p == endp) {
+        return s_n_llhttp__internal__n_start;
+      }
+      switch (*p) {
+        case 10: {
+          p++;
+          goto s_n_llhttp__internal__n_start;
+        }
+        case 13: {
+          p++;
+          goto s_n_llhttp__internal__n_start;
+        }
+        default: {
+          goto s_n_llhttp__internal__n_invoke_update_finish;
+        }
+      }
+      /* UNREACHABLE */;
+      abort();
+    }
+    default:
+      /* UNREACHABLE */
+      abort();
+  }
+  s_n_llhttp__internal__n_error_31: {
+    state->error = 0x7;
+    state->reason = "Invalid characters in url";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause_5: {
+    state->error = 0x14;
+    state->reason = "on_message_complete pause";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_9: {
+    state->error = 0x11;
+    state->reason = "`on_message_complete` callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause_7: {
+    state->error = 0x14;
+    state->reason = "on_chunk_complete pause";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_11: {
+    state->error = 0x13;
+    state->reason = "`on_chunk_complete` callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: {
+    switch (llhttp__on_chunk_complete(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+      case 20:
+        goto s_n_llhttp__internal__n_pause_7;
+      default:
+        goto s_n_llhttp__internal__n_error_11;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_10: {
+    state->error = 0x4;
+    state->reason = "Content-Length can't be present with chunked encoding";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause_2: {
+    state->error = 0x14;
+    state->reason = "on_message_complete pause";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_3: {
+    state->error = 0x11;
+    state->reason = "`on_message_complete` callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: {
+    switch (llhttp__on_message_complete(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_pause_1;
+      case 20:
+        goto s_n_llhttp__internal__n_pause_2;
+      default:
+        goto s_n_llhttp__internal__n_error_3;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_7: {
+    state->error = 0xc;
+    state->reason = "Chunk size overflow";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause_3: {
+    state->error = 0x14;
+    state->reason = "on_chunk_complete pause";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_5: {
+    state->error = 0x13;
+    state->reason = "`on_chunk_complete` callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: {
+    switch (llhttp__on_chunk_complete(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_invoke_update_content_length;
+      case 20:
+        goto s_n_llhttp__internal__n_pause_3;
+      default:
+        goto s_n_llhttp__internal__n_error_5;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_body: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_body(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_chunk_data_almost_done;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags: {
+    switch (llhttp__internal__c_or_flags(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_field_start;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause_4: {
+    state->error = 0x14;
+    state->reason = "on_chunk_header pause";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_4: {
+    state->error = 0x12;
+    state->reason = "`on_chunk_header` callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: {
+    switch (llhttp__on_chunk_header(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_invoke_is_equal_content_length;
+      case 20:
+        goto s_n_llhttp__internal__n_pause_4;
+      default:
+        goto s_n_llhttp__internal__n_error_4;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_6: {
+    state->error = 0xc;
+    state->reason = "Invalid character in chunk size";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_mul_add_content_length: {
+    switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) {
+      case 1:
+        goto s_n_llhttp__internal__n_error_7;
+      default:
+        goto s_n_llhttp__internal__n_chunk_size;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_8: {
+    state->error = 0xc;
+    state->reason = "Invalid character in chunk size";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_body_1: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_body(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_finish_1: {
+    switch (llhttp__internal__c_update_finish_1(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_eof;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause: {
+    state->error = 0x14;
+    state->reason = "on_message_complete pause";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_2: {
+    state->error = 0x11;
+    state->reason = "`on_message_complete` callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: {
+    switch (llhttp__on_message_complete(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+      case 20:
+        goto s_n_llhttp__internal__n_pause;
+      default:
+        goto s_n_llhttp__internal__n_error_2;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_1: {
+    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_2: {
+    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_upgrade: {
+    switch (llhttp__internal__c_update_upgrade(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_or_flags_2;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause_6: {
+    state->error = 0x14;
+    state->reason = "Paused by on_headers_complete";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_1: {
+    state->error = 0x10;
+    state->reason = "User callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: {
+    switch (llhttp__on_headers_complete(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+      case 1:
+        goto s_n_llhttp__internal__n_invoke_or_flags_1;
+      case 2:
+        goto s_n_llhttp__internal__n_invoke_update_upgrade;
+      case 20:
+        goto s_n_llhttp__internal__n_pause_6;
+      default:
+        goto s_n_llhttp__internal__n_error_1;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: {
+    switch (llhttp__before_headers_complete(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_test_flags_1: {
+    switch (llhttp__internal__c_test_flags_1(state, p, endp)) {
+      case 1:
+        goto s_n_llhttp__internal__n_error_10;
+      default:
+        goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_test_flags: {
+    switch (llhttp__internal__c_test_flags(state, p, endp)) {
+      case 1:
+        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1;
+      default:
+        goto s_n_llhttp__internal__n_invoke_test_flags_1;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_header_value: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_header_value(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_header_field_start;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state: {
+    switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_3: {
+    switch (llhttp__internal__c_or_flags_3(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_4: {
+    switch (llhttp__internal__c_or_flags_4(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_5: {
+    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_6: {
+    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_load_header_state: {
+    switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+      case 5:
+        goto s_n_llhttp__internal__n_invoke_or_flags_3;
+      case 6:
+        goto s_n_llhttp__internal__n_invoke_or_flags_4;
+      case 7:
+        goto s_n_llhttp__internal__n_invoke_or_flags_5;
+      case 8:
+        goto s_n_llhttp__internal__n_invoke_or_flags_6;
+      default:
+        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_1: {
+    switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_field_start;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_7: {
+    switch (llhttp__internal__c_or_flags_3(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state_1;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_8: {
+    switch (llhttp__internal__c_or_flags_4(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state_1;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_9: {
+    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state_1;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_10: {
+    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_field_start;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_load_header_state_2: {
+    switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+      case 5:
+        goto s_n_llhttp__internal__n_invoke_or_flags_7;
+      case 6:
+        goto s_n_llhttp__internal__n_invoke_or_flags_8;
+      case 7:
+        goto s_n_llhttp__internal__n_invoke_or_flags_9;
+      case 8:
+        goto s_n_llhttp__internal__n_invoke_or_flags_10;
+      default:
+        goto s_n_llhttp__internal__n_header_field_start;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_12: {
+    state->error = 0x3;
+    state->reason = "Missing expected LF after header value";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_header_value(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_header_value_almost_done;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_header_value(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) (p + 1);
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+      return s_error;
+    }
+    p++;
+    goto s_n_llhttp__internal__n_header_value_almost_done;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_13: {
+    state->error = 0xa;
+    state->reason = "Invalid header value char";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_3: {
+    switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_connection;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_11: {
+    switch (llhttp__internal__c_or_flags_3(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state_3;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_12: {
+    switch (llhttp__internal__c_or_flags_4(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state_3;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_13: {
+    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state_3;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_14: {
+    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_connection;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_load_header_state_3: {
+    switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+      case 5:
+        goto s_n_llhttp__internal__n_invoke_or_flags_11;
+      case 6:
+        goto s_n_llhttp__internal__n_invoke_or_flags_12;
+      case 7:
+        goto s_n_llhttp__internal__n_invoke_or_flags_13;
+      case 8:
+        goto s_n_llhttp__internal__n_invoke_or_flags_14;
+      default:
+        goto s_n_llhttp__internal__n_header_value_connection;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_4: {
+    switch (llhttp__internal__c_update_header_state_4(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_connection_token;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_2: {
+    switch (llhttp__internal__c_update_header_state_2(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_connection_ws;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_5: {
+    switch (llhttp__internal__c_update_header_state_5(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_connection_ws;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_6: {
+    switch (llhttp__internal__c_update_header_state_6(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_connection_ws;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_header_value(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_15;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_error_15;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_mul_add_content_length_1: {
+    switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) {
+      case 1:
+        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3;
+      default:
+        goto s_n_llhttp__internal__n_header_value_content_length;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_7: {
+    switch (llhttp__internal__c_update_header_state_4(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_15: {
+    switch (llhttp__internal__c_or_flags_15(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_discard_rws;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_header_value(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_16;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_error_16;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_14: {
+    state->error = 0x4;
+    state->reason = "Duplicate Content-Length";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_test_flags_2: {
+    switch (llhttp__internal__c_test_flags_2(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_header_value_content_length;
+      default:
+        goto s_n_llhttp__internal__n_error_14;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_8: {
+    switch (llhttp__internal__c_update_header_state_8(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_value_discard_rws;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_or_flags_16: {
+    switch (llhttp__internal__c_or_flags_16(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_header_state_7;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_load_header_state_1: {
+    switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+      case 1:
+        goto s_n_llhttp__internal__n_header_value_connection;
+      case 2:
+        goto s_n_llhttp__internal__n_invoke_test_flags_2;
+      case 3:
+        goto s_n_llhttp__internal__n_header_value_te_chunked;
+      case 4:
+        goto s_n_llhttp__internal__n_invoke_or_flags_16;
+      default:
+        goto s_n_llhttp__internal__n_header_value;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_header_field: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_header_field(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) (p + 1);
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_discard_ws;
+      return s_error;
+    }
+    p++;
+    goto s_n_llhttp__internal__n_header_value_discard_ws;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_header_field(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) (p + 1);
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_discard_ws;
+      return s_error;
+    }
+    p++;
+    goto s_n_llhttp__internal__n_header_value_discard_ws;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_17: {
+    state->error = 0xa;
+    state->reason = "Invalid header token";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_9: {
+    switch (llhttp__internal__c_update_header_state_4(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_field_general;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_store_header_state: {
+    switch (llhttp__internal__c_store_header_state(state, p, endp, match)) {
+      default:
+        goto s_n_llhttp__internal__n_header_field_colon;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_header_state_10: {
+    switch (llhttp__internal__c_update_header_state_4(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_field_general;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_http_minor: {
+    switch (llhttp__internal__c_update_http_minor(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_header_field_start;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_http_major: {
+    switch (llhttp__internal__c_update_http_major(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_http_minor;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_3: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_18: {
+    state->error = 0x7;
+    state->reason = "Expected CRLF";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_4: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_19: {
+    state->error = 0x9;
+    state->reason = "Expected CRLF after version";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_store_http_minor: {
+    switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {
+      default:
+        goto s_n_llhttp__internal__n_req_http_end;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_20: {
+    state->error = 0x9;
+    state->reason = "Invalid minor version";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_21: {
+    state->error = 0x9;
+    state->reason = "Expected dot";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_store_http_major: {
+    switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {
+      default:
+        goto s_n_llhttp__internal__n_req_http_dot;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_22: {
+    state->error = 0x9;
+    state->reason = "Invalid major version";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_24: {
+    state->error = 0x8;
+    state->reason = "Expected HTTP/";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_23: {
+    state->error = 0x8;
+    state->reason = "Expected SOURCE method for ICE/x.x request";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_is_equal_method_1: {
+    switch (llhttp__internal__c_is_equal_method_1(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_error_23;
+      default:
+        goto s_n_llhttp__internal__n_req_http_major;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_5: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_6: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_7: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_8: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_25: {
+    state->error = 0x7;
+    state->reason = "Invalid char in url fragment start";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_9: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_10: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_11: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_26: {
+    state->error = 0x7;
+    state->reason = "Invalid char in url query";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_27: {
+    state->error = 0x7;
+    state->reason = "Invalid char in url path";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_1: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_2: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_12: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_13: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_url_14: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_url(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) p;
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+      return s_error;
+    }
+    goto s_n_llhttp__internal__n_url_skip_to_http;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_28: {
+    state->error = 0x7;
+    state->reason = "Double @ in url";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_29: {
+    state->error = 0x7;
+    state->reason = "Unexpected char in url server";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_30: {
+    state->error = 0x7;
+    state->reason = "Unexpected char in url server";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_32: {
+    state->error = 0x7;
+    state->reason = "Unexpected char in url schema";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_33: {
+    state->error = 0x7;
+    state->reason = "Unexpected char in url schema";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_34: {
+    state->error = 0x7;
+    state->reason = "Unexpected start char in url";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_is_equal_method: {
+    switch (llhttp__internal__c_is_equal_method(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1;
+      default:
+        goto s_n_llhttp__internal__n_span_start_llhttp__on_url;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_35: {
+    state->error = 0x6;
+    state->reason = "Expected space after method";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_store_method_1: {
+    switch (llhttp__internal__c_store_method(state, p, endp, match)) {
+      default:
+        goto s_n_llhttp__internal__n_req_first_space_before_url;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_43: {
+    state->error = 0x6;
+    state->reason = "Invalid method encountered";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_36: {
+    state->error = 0xd;
+    state->reason = "Response overflow";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_mul_add_status_code: {
+    switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {
+      case 1:
+        goto s_n_llhttp__internal__n_error_36;
+      default:
+        goto s_n_llhttp__internal__n_res_status_code;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_status: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_status(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) (p + 1);
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start;
+      return s_error;
+    }
+    p++;
+    goto s_n_llhttp__internal__n_header_field_start;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_span_end_llhttp__on_status_1: {
+    const unsigned char* start;
+    int err;
+    
+    start = state->_span_pos0;
+    state->_span_pos0 = NULL;
+    err = llhttp__on_status(state, start, p);
+    if (err != 0) {
+      state->error = err;
+      state->error_pos = (const char*) (p + 1);
+      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done;
+      return s_error;
+    }
+    p++;
+    goto s_n_llhttp__internal__n_res_line_almost_done;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_37: {
+    state->error = 0xd;
+    state->reason = "Invalid response status";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_status_code: {
+    switch (llhttp__internal__c_update_status_code(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_res_status_code;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_38: {
+    state->error = 0x9;
+    state->reason = "Expected space after version";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_store_http_minor_1: {
+    switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {
+      default:
+        goto s_n_llhttp__internal__n_res_http_end;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_39: {
+    state->error = 0x9;
+    state->reason = "Invalid minor version";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_40: {
+    state->error = 0x9;
+    state->reason = "Expected dot";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_store_http_major_1: {
+    switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {
+      default:
+        goto s_n_llhttp__internal__n_res_http_dot;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_41: {
+    state->error = 0x9;
+    state->reason = "Invalid major version";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_44: {
+    state->error = 0x8;
+    state->reason = "Expected HTTP/";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_type: {
+    switch (llhttp__internal__c_update_type(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_req_first_space_before_url;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_store_method: {
+    switch (llhttp__internal__c_store_method(state, p, endp, match)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_update_type;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error_42: {
+    state->error = 0x8;
+    state->reason = "Invalid word encountered";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_type_1: {
+    switch (llhttp__internal__c_update_type_1(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_res_http_major;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_type_2: {
+    switch (llhttp__internal__c_update_type(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_start_req;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_pause_8: {
+    state->error = 0x14;
+    state->reason = "on_message_begin pause";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_error: {
+    state->error = 0xf;
+    state->reason = "`on_message_begin` callback error";
+    state->error_pos = (const char*) p;
+    state->_current = (void*) (intptr_t) s_error;
+    return s_error;
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: {
+    switch (llhttp__on_message_begin(state, p, endp)) {
+      case 0:
+        goto s_n_llhttp__internal__n_invoke_load_type;
+      case 20:
+        goto s_n_llhttp__internal__n_pause_8;
+      default:
+        goto s_n_llhttp__internal__n_error;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+  s_n_llhttp__internal__n_invoke_update_finish: {
+    switch (llhttp__internal__c_update_finish(state, p, endp)) {
+      default:
+        goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin;
+    }
+    /* UNREACHABLE */;
+    abort();
+  }
+}
+
+int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) {
+  llparse_state_t next;
+
+  /* check lingering errors */
+  if (state->error != 0) {
+    return state->error;
+  }
+
+  /* restart spans */
+  if (state->_span_pos0 != NULL) {
+    state->_span_pos0 = (void*) p;
+  }
+  
+  next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp);
+  if (next == s_error) {
+    return state->error;
+  }
+  state->_current = (void*) (intptr_t) next;
+
+  /* execute spans */
+  if (state->_span_pos0 != NULL) {
+    int error;
+  
+    error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp);
+    if (error != 0) {
+      state->error = error;
+      state->error_pos = endp;
+      return error;
+    }
+  }
+  
+  return 0;
+}
\ No newline at end of file
index 4265f7f..2353ff6 100644 (file)
@@ -1,5 +1,8 @@
-Original Authors "mruby developers" are:
-   Yukihiro Matsumoto
+This is the (likely incomplete) list of "mruby developers".
+If you submit a patch to mruby, please add your name to the end
+of this list.
+
+   Yukihiro Matsumoto (Matz)
    SCSK KYUSHU CORPORATION
    Kyushu Institute of Technology
    Network Applied Communication Laboratory, Inc.
@@ -38,3 +41,4 @@ Original Authors "mruby developers" are:
    Christopher Aue
    Masahiro Wakame
    YAMAMOTO Masaya
+   KOBAYASHI Shuji
index 8492999..53de0cd 100644 (file)
@@ -2,5 +2,5 @@ LEGAL NOTICE INFORMATION
 ------------------------
 
 All the files in this distribution are covered under the MIT license
-(see the file MITL) except some files mentioned below:
+(see the file LICENSE) except some files mentioned below:
 
similarity index 96%
rename from third-party/mruby/MITL
rename to third-party/mruby/LICENSE
index 8d8c78f..ab54323 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) 2018 mruby developers
+Copyright (c) 2019 mruby developers
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
index 529091b..3e71ef7 100644 (file)
@@ -17,7 +17,7 @@ of the Ministry of Economy, Trade and Industry of Japan.
 
 ## How to get mruby
 
-The stable version 1.4.1 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.4.1.zip](https://github.com/mruby/mruby/archive/1.4.1.zip)
+The stable version 2.0.1 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/2.0.1.zip](https://github.com/mruby/mruby/archive/2.0.1.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)
 
@@ -59,7 +59,7 @@ how to use mrbgems look into the folder *examples/mrbgems/*.
 
 ## License
 
-mruby is released under the [MIT License](MITL).
+mruby is released under the [MIT License](LICENSE).
 
 ## Note for License
 
index 2f6fa05..5331532 100644 (file)
@@ -32,20 +32,25 @@ load "#{MRUBY_ROOT}/tasks/benchmark.rake"
 
 load "#{MRUBY_ROOT}/tasks/gitlab.rake"
 
+def install_D(src, dst)
+  opts = { :verbose => $verbose }
+  FileUtils.rm_f dst, opts
+  FileUtils.mkdir_p File.dirname(dst), opts
+  FileUtils.cp src, dst, opts
+end
+
 ##############################
 # 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 }
+    install_D t.prerequisites.first, t.name
   end
 
   install_path
@@ -65,7 +70,7 @@ MRuby.each_target do |target|
       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|
+      file exec => objs + target.libraries 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 }
@@ -78,8 +83,7 @@ MRuby.each_target do |target|
         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 }
+          install_D t.prerequisites.first, t.name
         end
         depfiles += [ install_path ]
       elsif target == MRuby.targets['host-debug']
@@ -87,8 +91,7 @@ MRuby.each_target do |target|
           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 }
+            install_D t.prerequisites.first, t.name
           end
           depfiles += [ install_path ]
         end
@@ -100,7 +103,7 @@ MRuby.each_target do |target|
 end
 
 depfiles += MRuby.targets.map { |n, t|
-  [t.libfile("#{t.build_dir}/lib/libmruby")]
+  t.libraries
 }.flatten
 
 depfiles += MRuby.targets.reject { |n, t| n == 'host' }.map { |n, t|
@@ -118,9 +121,22 @@ task :all => depfiles do
 end
 
 desc "run all mruby tests"
-task :test => ["all"] do
-  MRuby.each_target do
-    run_test if test_enabled?
+task :test
+MRuby.each_target do
+  if test_enabled?
+    t = :"test_#{self.name}"
+    task t => ["all"] do
+      run_test
+    end
+    task :test => t
+  end
+
+  if bintest_enabled?
+    t = :"bintest_#{self.name}"
+    task t => ["all"] do
+      run_bintest
+    end
+    task :test => t
   end
 end
 
index 3e195f9..6227b60 100644 (file)
@@ -2,8 +2,6 @@ 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)
 
index b4514ec..a91834c 100644 (file)
@@ -1,16 +1,19 @@
 version: "{build}"
 
-os: Visual Studio 2015
+os: Visual Studio 2017
 
-clone_depth: 50
+shallow_clone: true
 
 
 cache:
-  - win_flex_bison-2.5.10.zip
+  - win_flex_bison
 
 
 environment:
   matrix:
+    # Visual Studio 2017 64bit
+    - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat
+
     # Visual Studio 2015 64bit
     - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
       machine: amd64
@@ -28,11 +31,12 @@ init:
 
 
 install:
-  - if not exist win_flex_bison-2.5.10.zip appveyor DownloadFile "https://github.com/lexxmark/winflexbison/releases/download/v.2.5.10/win_flex_bison-2.5.10.zip"
-  - 7z x -y -owin_flex_bison win_flex_bison-2.5.10.zip > nul
-
+  - if not exist win_flex_bison (
+      appveyor DownloadFile "https://github.com/lexxmark/winflexbison/releases/download/v.2.5.10/win_flex_bison-2.5.10.zip" &
+      7z x -y -owin_flex_bison win_flex_bison-2.5.10.zip
+    )
 
 build_script:
   - set YACC=.\win_flex_bison\win_bison.exe
   - set MRUBY_CONFIG=appveyor_config.rb
-  - ruby .\minirake test all
+  - rake -E $stdout.sync=true test
index 26283cc..de82685 100644 (file)
@@ -48,5 +48,4 @@ answer = to_array(solution).map do |p|
   to_string(p)
 end
 
-answer_str = answer.to_a
-# puts answer_str
+# puts answer
diff --git a/third-party/mruby/bin/.gitkeep b/third-party/mruby/bin/.gitkeep
deleted file mode 100644 (file)
index e69de29..0000000
index 1429837..751317c 100644 (file)
@@ -8,7 +8,8 @@ MRuby::Build.new do |conf|
     toolchain :gcc
   end
 
-  enable_debug
+  # Turn on `enable_debug` for better debugging
+  # enable_debug
 
   # Use mrbgems
   # conf.gem 'examples/mrbgems/ruby_extension_example'
@@ -28,7 +29,7 @@ MRuby::Build.new do |conf|
   #   cc.command = ENV['CC'] || 'gcc'
   #   cc.flags = [ENV['CFLAGS'] || %w()]
   #   cc.include_paths = ["#{root}/include"]
-  #   cc.defines = %w(DISABLE_GEMS)
+  #   cc.defines = %w()
   #   cc.option_include_path = '-I%s'
   #   cc.option_define = '-D%s'
   #   cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}"
index 1cc7a9a..81c0e9d 100644 (file)
@@ -38,7 +38,7 @@ To confirm mrdb was installed properly, run mrdb with the `--version` option:
 
 ```bash
 $ mrdb --version
-mruby 1.4.1 (2018-4-27)
+mruby 2.0.1 (2019-4-4)
 ```
 
 ## 2.2 Basic Operation
index 8dac0dc..0fcc936 100644 (file)
@@ -179,11 +179,11 @@ Version requirement supports following operators:
 
 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*.
+You can have default gem to use as dependency 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,
+When a special version of dependency is required,
 use `MRuby::Build#gem` in *build_config.rb* to override default gem.
 
 If you have conflicting GEMs use the following method:
index 9d8924c..9b4ed9c 100644 (file)
@@ -38,7 +38,7 @@ puts [1,2,3]
 3
 ```
 
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
 
 ```
 [1, 2, 3]
@@ -61,7 +61,7 @@ end
 
 ```ZeroDivisionError``` is raised.
 
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
 
 No exception is raised.
 
@@ -89,7 +89,7 @@ p Liste.new "foobar"
 
 ``` [] ```
 
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
 
 ```ArgumentError``` is raised.
 
@@ -119,7 +119,7 @@ false
 true
 ```
 
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
 
 ```
 true
@@ -142,7 +142,7 @@ defined?(Foo)
 nil
 ```
 
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
 
 ```NameError``` is raised.
 
@@ -159,7 +159,7 @@ alias $a $__a__
 
 ``` nil ```
 
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
 
 Syntax error
 
@@ -181,7 +181,69 @@ end
 ```ArgumentError``` is raised.
 The re-defined ```+``` operator does not accept any arguments.
 
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
 
 ``` 'ab' ```
 Behavior of the operator wasn't changed.
+
+## Kernel#binding is not supported
+
+`Kernel#binding` method is not supported.
+
+#### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]
+
+```
+$ ruby -e 'puts Proc.new {}.binding'
+#<Binding:0x00000e9deabb9950>
+```
+
+#### mruby [2.0.1 (2019-4-4)]
+
+```
+$ ./bin/mruby -e 'puts Proc.new {}.binding'
+trace (most recent call last):
+        [0] -e:1
+-e:1: undefined method 'binding' (NoMethodError)
+```
+
+## Keyword arguments
+
+mruby keyword arguments behave slightly different from CRuby 2.5
+to make the behavior simpler and less confusing. Maybe in the
+future, the simpler behavior will be adopted to CRuby as well.
+
+#### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]
+
+```
+$ ruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
+[[{"a"=>1}], {:b=>2}]
+```
+
+#### mruby [mruby 2.0.1]
+
+```
+$ ./bin/mruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
+trace (most recent call last):
+       [0] -e:1
+-e:1: keyword argument hash with non symbol keys (ArgumentError)
+```
+
+## Argument Destructuring
+
+```ruby
+def m(a,(b,c),d); p [a,b,c,d]; end
+m(1,[2,3],4)  # => [1,2,3,4]
+```
+Destructured arguments (`b` and `c` in above example) cannot be accessed
+from the default expression of optional arguments and keyword arguments,
+since actual assignment is done after the evaluation of those default
+expressions. Thus:
+    
+```ruby
+def f(a,(b,c),d=b)
+  p [a,b,c,d]
+end
+f(1,[2,3])
+```
+
+CRuby gives `[1,2,3,nil]`. mruby raises `NoMethodError` for `b`.
diff --git a/third-party/mruby/doc/opcode.md b/third-party/mruby/doc/opcode.md
new file mode 100644 (file)
index 0000000..eab82a2
--- /dev/null
@@ -0,0 +1,127 @@
+# The new bytecode
+
+We will reimplement VM to use 8bit instruction code. By
+bytecode, we mean real byte code. The whole purpose is
+reducing the memory consumption of mruby VM.
+
+# Instructions
+
+Instructions are bytes. There can be 256 instructions. Currently we
+have 94 instructions. Instructions can take 0 to 3 operands.
+
+## operands
+
+The size of operands can be either 8bits, 16bits or 24bits.
+In the table.1 below, the second field describes the size (and
+sign) of operands.
+
+* B: 8bit
+* sB: signed 8bit
+* S: 16bit
+* sS: signed 16bit
+* W: 24bit
+
+First two byte operands may be extended to 16bit. When those byte
+operands are bigger than 256, the instruction will be prefixed by
+`OP_EXT1` (means 1st operand is 16bit) or `OP_EXT2` (means 2nd operand
+is 16bit) or `OP_EXT3` (means 1st and 2nd operands are 16bit).
+
+For instructions marked by `'`, `OP_EXT1` can be prefixed. For those
+with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed.
+
+## table.1 Instruction Table
+
+|Instruction Name |Operand type |Semantics        
+|-----------------|-------------|-----------------
+|OP_NOP           | -           |                 
+|OP_MOVE"         |BB           |R(a) = R(b)      
+|OP_LOADL"        |BB           |R(a) = Pool(b)   
+|OP_LOADI"        |BsB          |R(a) = mrb_int(b)
+|OP_LOADI_0'      |B            |R(a) = 0
+|OP_LOADI_1'      |B            |R(a) = 1
+|OP_LOADI_2'      |B            |R(a) = 2
+|OP_LOADI_3'      |B            |R(a) = 3
+|OP_LOADSYM"      |BB           |R(a) = Syms(b)
+|OP_LOADNIL'      |B            |R(a) = nil
+|OP_LOADSELF'     |B            |R(a) = self
+|OP_LOADT'        |B            |R(a) = true
+|OP_LOADF'        |B            |R(a) = false
+|OP_GETGV"        |BB           |R(a) = getglobal(Syms(b))
+|OP_SETGV"        |BB           |setglobal(Syms(b), R(a))
+|OP_GETSV"        |BB           |R(a) = Special[b]
+|OP_SETSV"        |BB           |Special[b] = R(a)
+|OP_GETIV"        |BB           |R(a) = ivget(Syms(b))
+|OP_SETIV"        |BB           |ivset(Syms(b),R(a))
+|OP_GETCV"        |BB           |R(a) = cvget(Syms(b))
+|OP_SETCV"        |BB           |cvset(Syms(b),R(a))
+|OP_GETCONST"     |BB           |R(a) = constget(Syms(b))
+|OP_SETCONST"     |BB           |constset(Syms(b),R(a))
+|OP_GETMCNST"     |BB           |R(a) = R(a)::Syms(b)
+|OP_SETMCNST"     |BB           |R(a+1)::Syms(b) = R(a)
+|OP_GETUPVAR'     |BBB          |R(a) = uvget(b,c)
+|OP_SETUPVAR'     |BBB          |uvset(b,c,R(a))
+|OP_JMP           |S            |pc+=a
+|OP_JMPIF'        |SB           |if R(b) pc+=a
+|OP_JMPNOT'       |SB           |if !R(b) pc+=a
+|OP_ONERR         |sS           |rescue_push(pc+a)
+|OP_EXCEPT'       |B            |R(a) = exc
+|OP_RESCUE"       |BB           |R(b) = R(a).isa?(R(b))
+|OP_POPERR        |B            |a.times{rescue_pop()}
+|OP_RAISE'        |B            |raise(R(a))
+|OP_EPUSH'        |B            |ensure_push(SEQ[a])
+|OP_EPOP          |B            |A.times{ensure_pop().call}
+|OP_SENDV"        |BB           |R(a) = call(R(a),Syms(b),*R(a+1))
+|OP_SENDVB"       |BB           |R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2))
+|OP_SEND"         |BBB          |R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c))
+|OP_SENDB"        |BBB          |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1))
+|OP_CALL'         |B            |R(a) = self.call(frame.argc, frame.argv)
+|OP_SUPER'        |BB           |R(a) = super(R(a+1),... ,R(a+b+1))
+|OP_ARGARY'       |BS           |R(a) = argument array (16=5:1:5:1:4)
+|OP_ENTER         |W            |arg setup according to flags (23=5:5:1:5:5:1:1)
+|OP_KARG"         |BB           |R(a) = kdict[Syms(Bx)]                          # todo
+|OP_KARG2"        |BB           |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b))       # todo
+|OP_RETURN'       |B            |return R(a) (normal)
+|OP_RETURN_BLK'   |B            |return R(a) (in-block return)
+|OP_BREAK'        |B            |break R(a)
+|OP_BLKPUSH'      |BS           |R(a) = block (16=5:1:5:1:4)
+|OP_ADD"          |BB           |R(a) = R(a)+R(a+1)
+|OP_ADDI"         |BBB          |R(a) = R(a)+mrb_int(c)
+|OP_SUB"          |BB           |R(a) = R(a)-R(a+1)
+|OP_SUBI"         |BB           |R(a) = R(a)-C
+|OP_MUL"          |BB           |R(a) = R(a)*R(a+1)
+|OP_DIV"          |BB           |R(a) = R(a)/R(a+1)
+|OP_EQ"           |BB           |R(a) = R(a)==R(a+1)
+|OP_LT"           |BB           |R(a) = R(a)<R(a+1)
+|OP_LE"           |BB           |R(a) = R(a)<=R(a+1)
+|OP_GT"           |BB           |R(a) = R(a)>R(a+1)
+|OP_GE"           |BB           |R(a) = R(a)>=R(a+1)
+|OP_ARRAY'        |BB           |R(a) = ary_new(R(a),R(a+1)..R(a+b))
+|OP_ARRAY2"       |BB           |R(a) = ary_new(R(b),R(b+1)..R(b+c))
+|OP_ARYCAT'       |B            |ary_cat(R(a),R(a+1))
+|OP_ARYPUSH'      |B            |ary_push(R(a),R(a+1))
+|OP_AREF'         |BB           |R(a) = R(a)[b]
+|OP_ASET'         |BB           |R(a)[b] = R(a+1)
+|OP_APOST'        |BB           |*R(a),R(A+1)..R(A+C) = R(a)[B..]
+|OP_STRING"       |BB           |R(a) = str_dup(Lit(b))
+|OP_STRCAT'       |B            |str_cat(R(a),R(a+1))
+|OP_HASH'         |BB           |R(a) = hash_new(R(a),R(a+1)..R(a+b))
+|OP_HASHADD'      |BB           |R(a) = hash_push(R(a),R(a+1)..R(a+b))
+|OP_LAMBDA"       |BB           |R(a) = lambda(SEQ[b],OP_L_LAMBDA)
+|OP_BLOCK"        |BB           |R(a) = lambda(SEQ[b],OP_L_BLOCK)
+|OP_METHOD"       |BB           |R(a) = lambda(SEQ[b],OP_L_METHOD)
+|OP_RANGE_INC'    |B            |R(a) = range_new(R(a),R(a+1),FALSE)
+|OP_RANGE_EXC'    |B            |R(a) = range_new(R(a),R(a+1),TRUE)
+|OP_OCLASS'       |B            |R(a) = ::Object
+|OP_CLASS"        |BB           |R(a) = newclass(R(a),Syms(b),R(a+1))
+|OP_MODULE"       |BB           |R(a) = newmodule(R(a),Syms(b))
+|OP_EXEC"         |BB           |R(a) = blockexec(R(a),SEQ[b])
+|OP_DEF"          |BB           |R(a).newmethod(Syms(b),R(a+1))
+|OP_ALIAS'        |B            |alias_method(R(a),R(a+1),R(a+2))
+|OP_UNDEF"        |BB           |undef_method(R(a),Syms(b))
+|OP_SCLASS'       |B            |R(a) = R(a).singleton_class
+|OP_TCLASS'       |B            |R(a) = target_class
+|OP_ERR'          |B            |raise(RuntimeError, Lit(Bx))
+|OP_EXT1          |-            |make 1st operand 16bit
+|OP_EXT2          |-            |make 2nd operand 16bit
+|OP_EXT3          |-            |make 1st and 2nd operands 16bit
+|OP_STOP          |-            |stop VM
index 6188c13..70b0f4b 100644 (file)
@@ -15,8 +15,8 @@ end
 
 # Requires Android NDK r13 or later.
 MRuby::CrossBuild.new('android-arm64-v8a') do |conf|
-  params = { 
-    :arch => 'arm64-v8a', 
+  params = {
+    :arch => 'arm64-v8a',
     :platform => 'android-24',
     :toolchain => :clang,
   }
index b7eb33a..1733024 100644 (file)
@@ -15,8 +15,8 @@ end
 
 # Requires Android NDK r13 or later.
 MRuby::CrossBuild.new('android-armeabi') do |conf|
-  params = { 
-    :arch => 'armeabi', 
+  params = {
+    :arch => 'armeabi',
     :platform => 'android-24',
     :toolchain => :clang,
   }
index cc28acf..08e69d3 100644 (file)
 # endif
 #endif
 
+/* define on big endian machines; used by MRB_NAN_BOXING, etc. */
+#ifndef MRB_ENDIAN_BIG
+# if (defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN) || \
+     (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#  define MRB_ENDIAN_BIG
+# endif
+#endif
+
 /* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT and MRB_WITHOUT_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
 
 
 /* -DMRB_ENABLE_XXXX to enable following features */
 //#define MRB_ENABLE_DEBUG_HOOK /* hooks for debugger */
+//#define MRB_ENABLE_ALL_SYMBOLS /* Symbols.all_symbols */
 
 /* end of configuration */
 
index 3b953a3..5b0d84c 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ** mruby - An embeddable Ruby implementation
 **
-** Copyright (c) mruby developers 2010-2018
+** Copyright (c) mruby developers 2010-2019
 **
 ** Permission is hereby granted, free of charge, to any person obtaining
 ** a copy of this software and associated documentation files (the
@@ -34,6 +34,7 @@
 #define __STDC_FORMAT_MACROS
 #endif
 
+#include <stdarg.h>
 #include <stdint.h>
 #include <stddef.h>
 #include <limits.h>
@@ -57,7 +58,7 @@
 #define mrb_assert_int_fit(t1,n,t2,max) ((void)0)
 #endif
 
-#if __STDC_VERSION__ >= 201112L
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L
 #define mrb_static_assert(exp, str) _Static_assert(exp, str)
 #else
 #define mrb_static_assert(exp, str) mrb_assert(exp)
@@ -93,7 +94,7 @@
  */
 MRB_BEGIN_DECL
 
-typedef uint32_t mrb_code;
+typedef uint8_t mrb_code;
 
 /**
  * Required arguments signature type.
@@ -123,9 +124,8 @@ typedef struct {
   mrb_sym mid;
   struct RProc *proc;
   mrb_value *stackent;
-  int nregs;
-  int ridx;
-  int epos;
+  uint16_t ridx;
+  uint16_t epos;
   struct REnv *env;
   mrb_code *pc;                 /* return address */
   mrb_code *err;                /* error position */
@@ -152,10 +152,10 @@ struct mrb_context {
   mrb_callinfo *ci;
   mrb_callinfo *cibase, *ciend;
 
-  mrb_code **rescue;                      /* exception handler stack */
-  int rsize;
+  uint16_t *rescue;                       /* exception handler stack */
+  uint16_t rsize;
   struct RProc **ensure;                  /* ensure handler stack */
-  int esize, eidx;
+  uint16_t esize, eidx;
 
   enum mrb_fiber_state status;
   mrb_bool vmexec;
@@ -240,9 +240,12 @@ typedef struct mrb_state {
 #endif
 
   mrb_sym symidx;
-  struct kh_n2s *name2sym;      /* symbol hash */
   struct symbol_name *symtbl;   /* symbol table */
+  mrb_sym symhash[256];
   size_t symcapa;
+#ifndef MRB_ENABLE_SYMBOLL_ALL
+  char symbuf[8];               /* buffer for small symbol names */
+#endif
 
 #ifdef MRB_ENABLE_DEBUG_HOOK
   void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
@@ -268,7 +271,8 @@ typedef struct mrb_state {
 #else
   mrb_atexit_func *atexit_stack;
 #endif
-  mrb_int atexit_stack_len;
+  uint16_t atexit_stack_len;
+  uint16_t ecall_nest;                    /* prevent infinite recursive ecall() */
 } mrb_state;
 
 /**
@@ -386,7 +390,7 @@ MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *,
 MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec);
 
 /**
- *  Defines a module fuction.
+ *  Defines a module function.
  *
  * Example:
  *
@@ -486,9 +490,10 @@ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, 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.
+ * @param [const char*] const char* The name of the method to be undefined.
  */
 MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
+MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym);
 
 /**
  * Undefine a class method.
@@ -525,12 +530,12 @@ MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
  *      }
  * @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.
+ * @param [const char*] const char* 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.
+ * Initialize a new object instance of c class.
  *
  * Example:
  *
@@ -704,6 +709,9 @@ MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name);
  * @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);
+/* a function to raise NotImplementedError with current method name */
+MRB_API void mrb_notimplement(mrb_state*);
+/* a function to be replacement of unimplemented method */
 MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
 
 /**
@@ -716,7 +724,6 @@ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
  * @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
@@ -783,7 +790,7 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
 #define MRB_ARGS_REQ(n)     ((mrb_aspec)((n)&0x1f) << 18)
 
 /**
- * Funtion takes n optional arguments
+ * Function takes n optional arguments
  *
  * @param n
  *      The number of optional arguments.
@@ -791,7 +798,7 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
 #define MRB_ARGS_OPT(n)     ((mrb_aspec)((n)&0x1f) << 13)
 
 /**
- * Funtion takes n1 mandatory arguments and n2 optional arguments
+ * Function takes n1 mandatory arguments and n2 optional arguments
  *
  * @param n1
  *      The number of required arguments.
@@ -855,10 +862,6 @@ 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.
@@ -994,13 +997,13 @@ 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, int len);
-char* mrb_locale_from_utf8(const char *p, int len);
+MRB_API char* mrb_utf8_from_locale(const char *p, int len);
+MRB_API char* mrb_locale_from_utf8(const char *p, int len);
 #define mrb_locale_free(p) free(p)
 #define mrb_utf8_free(p) free(p)
 #else
-#define mrb_utf8_from_locale(p, l) ((char*)p)
-#define mrb_locale_from_utf8(p, l) ((char*)p)
+#define mrb_utf8_from_locale(p, l) ((char*)(p))
+#define mrb_locale_from_utf8(p, l) ((char*)(p))
 #define mrb_locale_free(p)
 #define mrb_utf8_free(p)
 #endif
@@ -1144,6 +1147,8 @@ 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);
+/* function for `raisef` formatting */
+MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap);
 
 /* macros to get typical exception objects
    note:
@@ -1188,6 +1193,7 @@ 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 mrb_value mrb_to_str(mrb_state *mrb, mrb_value val);
 MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t);
 
 typedef enum call_type {
@@ -1197,7 +1203,7 @@ typedef enum call_type {
   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 void mrb_define_alias(mrb_state *mrb, struct RClass *c, const char *a, const char *b);
 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);
 
@@ -1235,6 +1241,7 @@ MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib);
  * @mrbgem mruby-fiber
  */
 #define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError"))
+MRB_API void mrb_stack_extend(mrb_state*, mrb_int);
 
 /* memory pool implementation */
 typedef struct mrb_pool mrb_pool;
index 6fffe45..2e6951c 100644 (file)
@@ -199,6 +199,7 @@ MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val
  * @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_ensure_array_type(mrb_state *mrb, mrb_value self);
 MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
 
 /*
@@ -227,6 +228,22 @@ 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);
 
 /*
+ * Replace subsequence of an array.
+ *
+ * Equivalent to:
+ *
+ *      ary.shift
+ *
+ * @param mrb The mruby state reference.
+ * @param self The array from which the value will be shifted.
+ * @param head Beginning position of a replacement subsequence.
+ * @param len Length of a replacement subsequence.
+ * @param rpl The array of replacement elements.
+ * @return The receiver array.
+ */
+MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value self, mrb_int head, mrb_int len, mrb_value rpl);
+
+/*
  * Shifts the first element from the array.
  *
  * Equivalent to:
index 3d9bbdc..ff6f608 100644 (file)
@@ -81,7 +81,7 @@ typedef struct mrb_value {
 } while (0)
 
 #define SET_FLOAT_VALUE(mrb,r,v) do { \
-  if (v != v) { \
+  if ((v) != (v)) { \
     (r).value.ttt = 0x7ff80000; \
     (r).value.i = 0; \
   } \
index b17bfc9..3b7167b 100644 (file)
@@ -45,6 +45,14 @@ enum mrb_special_consts {
 #define MRB_SYMBOL_FLAG   0x0e
 #define MRB_SPECIAL_SHIFT 8
 
+#if defined(MRB_64BIT)
+#define MRB_SYMBOL_BITSIZE  (sizeof(mrb_sym) * CHAR_BIT)
+#define MRB_SYMBOL_MAX      UINT32_MAX
+#else
+#define MRB_SYMBOL_BITSIZE  (sizeof(mrb_sym) * CHAR_BIT - MRB_SPECIAL_SHIFT)
+#define MRB_SYMBOL_MAX      (UINT32_MAX >> MRB_SPECIAL_SHIFT)
+#endif
+
 typedef union mrb_value {
   union {
     void *p;
@@ -54,7 +62,7 @@ typedef union mrb_value {
     };
     struct {
       unsigned int sym_flag : MRB_SPECIAL_SHIFT;
-      mrb_sym sym : (sizeof(mrb_sym) * CHAR_BIT);
+      mrb_sym sym : MRB_SYMBOL_BITSIZE;
     };
     struct RBasic *bp;
 #ifndef MRB_WITHOUT_FLOAT
@@ -121,13 +129,13 @@ mrb_type(mrb_value o)
 } while (0)
 
 #ifndef MRB_WITHOUT_FLOAT
-#define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v)
+#define SET_FLOAT_VALUE(mrb,r,v) ((r) = mrb_word_boxing_float_value(mrb, v))
 #endif
-#define SET_CPTR_VALUE(mrb,r,v) r = mrb_word_boxing_cptr_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_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))
index ea35d8e..b667e20 100644 (file)
@@ -22,9 +22,6 @@ struct RClass {
 };
 
 #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)
@@ -53,22 +50,28 @@ mrb_class(mrb_state *mrb, mrb_value v)
   }
 }
 
-/* TODO: figure out where to put user flags */
-/* flags bits >= 18 is reserved */
-#define MRB_FLAG_IS_PREPENDED (1 << 19)
-#define MRB_FLAG_IS_ORIGIN (1 << 20)
+/* flags:
+   20: frozen
+   19: is_prepended
+   18: is_origin
+   17: is_inherited (used by method cache)
+   16: unused
+   0-15: instance type
+*/
+#define MRB_FL_CLASS_IS_PREPENDED (1 << 19)
+#define MRB_FL_CLASS_IS_ORIGIN (1 << 18)
 #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;\
+  if ((c)->flags & MRB_FL_CLASS_IS_PREPENDED) {\
+    (c) = (c)->super;\
+    while (!((c)->flags & MRB_FL_CLASS_IS_ORIGIN)) {\
+      (c) = (c)->super;\
     }\
   }\
 } while (0)
-#define MRB_FLAG_IS_INHERITED (1 << 21)
+#define MRB_FL_CLASS_IS_INHERITED (1 << 17)
 #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)
+#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);
@@ -76,7 +79,7 @@ MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb
 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, mrb_method_t);
 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 void mrb_alias_method(mrb_state*, struct RClass *c, mrb_sym a, mrb_sym b);
 
 MRB_API mrb_method_t mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
 MRB_API mrb_method_t mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
index d6ec78b..4eaac9a 100644 (file)
@@ -7,6 +7,11 @@
 #ifndef MRUBY_COMMON_H
 #define MRUBY_COMMON_H
 
+#ifdef __APPLE__
+  #ifndef __TARGETCONDITIONALS__
+  #include "TargetConditionals.h"
+  #endif
+#endif
 
 #ifdef __cplusplus
 #ifdef MRB_ENABLE_CXX_ABI
@@ -29,7 +34,7 @@
 MRB_BEGIN_DECL
 
 /** Declare a function that never returns. */
-#if __STDC_VERSION__ >= 201112L
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L
 # define mrb_noreturn _Noreturn
 #elif defined __GNUC__ && !defined __STRICT_ANSI__
 # define mrb_noreturn __attribute__((noreturn))
index d7d0296..f19d9b0 100644 (file)
@@ -33,6 +33,7 @@ typedef struct mrbc_context {
   mrb_bool no_exec:1;
   mrb_bool keep_lv:1;
   mrb_bool no_optimize:1;
+  mrb_bool on_eval:1;
 
   size_t parser_nerr;
 } mrbc_context;
@@ -117,7 +118,7 @@ struct mrb_parser_state {
   FILE *f;
 #endif
   mrbc_context *cxt;
-  char const *filename;
+  mrb_sym filename_sym;
   int lineno;
   int column;
 
@@ -142,7 +143,6 @@ struct mrb_parser_state {
   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;
 
@@ -151,13 +151,14 @@ struct mrb_parser_state {
   mrb_ast_node *tree;
 
   mrb_bool no_optimize:1;
+  mrb_bool on_eval: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;
+  uint16_t filename_table_length;
+  uint16_t current_filename_index;
 
   struct mrb_jmpbuf* jmp;
 };
@@ -167,7 +168,7 @@ MRB_API void mrb_parser_free(struct mrb_parser_state*);
 MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
 
 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);
+MRB_API mrb_sym mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
 
 /* utility functions */
 #ifndef MRB_DISABLE_STDIO
index 5904705..4156843 100644 (file)
@@ -39,10 +39,11 @@ MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass
 #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);\
+#define Data_Make_Struct(mrb,klass,strct,type,sval,data_obj) do { \
+  (data_obj) = Data_Wrap_Struct(mrb,klass,type,NULL);\
+  (sval) = mrb_malloc(mrb, sizeof(strct));                     \
+  { static const strct zero = { 0 }; *(sval) = zero; };\
+  (data_obj)->data = (sval);\
 } while (0)
 
 #define RDATA(obj)         ((struct RData *)(mrb_ptr(obj)))
index d1de348..e08c47c 100644 (file)
@@ -26,7 +26,6 @@ typedef struct 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;
@@ -47,18 +46,19 @@ typedef struct 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, ptrdiff_t pc);
+MRB_API const char *mrb_debug_get_filename(mrb_state *mrb, mrb_irep *irep, ptrdiff_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, ptrdiff_t pc);
+MRB_API int32_t mrb_debug_get_line(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc);
 
+MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep);
 MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file(
-    mrb_state *mrb, mrb_irep *irep,
+    mrb_state *mrb, mrb_irep_debug_info *info,
+    const char *filename, uint16_t *lines,
     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
index f56d66a..0234a36 100644 (file)
@@ -52,11 +52,11 @@ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
 /* Rite Binary File header */
 #define RITE_BINARY_IDENT              "RITE"
 #define RITE_BINARY_IDENT_LIL          "ETIR"
-#define RITE_BINARY_FORMAT_VER         "0004"
+#define RITE_BINARY_FORMAT_VER         "0006"
 #define RITE_COMPILER_NAME             "MATZ"
 #define RITE_COMPILER_VERSION          "0000"
 
-#define RITE_VM_VER                    "0000"
+#define RITE_VM_VER                    "0002"
 
 #define RITE_BINARY_EOF                "END\0"
 #define RITE_SECTION_IREP_IDENT        "IREP"
index 1587795..237c701 100644 (file)
@@ -29,7 +29,7 @@ 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 */
+/* declaration for `fail` method */
 MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
 
 struct RBreak {
index 1a87078..7e2ed55 100644 (file)
@@ -8,7 +8,6 @@
 #define MRUBY_HASH_H
 
 #include "common.h"
-#include <mruby/khash.h>
 
 /**
  * Hash class
@@ -18,13 +17,15 @@ MRB_BEGIN_DECL
 struct RHash {
   MRB_OBJECT_HEADER;
   struct iv_tbl *iv;
-  struct kh_ht *ht;
+  struct htable *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);
+MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash);
+MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
 
 /*
  * Initializes a new hash.
@@ -74,7 +75,7 @@ MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
  *
  * Equivalent to:
  *
- *     hash.hash_key?(key) ? hash[key] : def
+ *     hash.key?(key) ? hash[key] : def
  *
  * @param mrb The mruby state reference.
  * @param hash The target hash.
@@ -110,7 +111,19 @@ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value
  * @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 has the key.
+ *
+ * Equivalent to:
+ *
+ *     hash.key?(key)
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to check existence.
+ * @return True if the hash has the key
+ */
+MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key);
 
 /*
  * Check if the hash is empty
@@ -123,7 +136,7 @@ MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
  * @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);
+MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
 
 /*
  * Gets an array of values.
@@ -151,21 +164,51 @@ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash);
  */
 MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
 
-/* declaration of struct kh_ht */
+/*
+ * Get hash size.
+ *
+ * Equivalent to:
+ *
+ *      hash.size
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return The hash size.
+ */
+MRB_API mrb_int mrb_hash_size(mrb_state *mrb, mrb_value hash);
+
+/*
+ * Copies the hash.
+ *
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return The copy of the hash
+ */
+MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value hash);
+
+/*
+ * Merges two hashes. The first hash will be modified by the
+ * second hash.
+ *
+ * @param mrb The mruby state reference.
+ * @param hash1 The target hash.
+ * @param hash2 Updating hash
+ */
+MRB_API void mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2);
+
+/* declaration of struct mrb_hash_value */
 /* 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
@@ -177,6 +220,10 @@ 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*);
 
+/* return non zero to break the loop */
+typedef int (mrb_hash_foreach_func)(mrb_state *mrb, mrb_value key, mrb_value val, void *data);
+MRB_API void mrb_hash_foreach(mrb_state *mrb, struct RHash *hash, mrb_hash_foreach_func *func, void *p);
+
 MRB_END_DECL
 
 #endif  /* MRUBY_HASH_H */
index efd2267..027a294 100644 (file)
@@ -39,23 +39,36 @@ typedef struct mrb_irep {
 
   struct mrb_locals *lv;
   /* debug info */
-  mrb_bool own_filename;
-  const char *filename;
-  uint16_t *lines;
   struct mrb_irep_debug_info* debug_info;
 
-  int ilen, plen, slen, rlen, refcnt;
+  uint16_t ilen, plen, slen, rlen;
+  uint32_t refcnt;
 } mrb_irep;
 
 #define MRB_ISEQ_NO_FREE 1
 
 MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb);
+
+/* @param [const uint8_t*] irep code, expected as a literal */
 MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*);
+
+/* @param [const uint8_t*] irep code, expected as a literal */
 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*);
 void mrb_irep_cutref(mrb_state*, struct mrb_irep*);
+void mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep);
+
+struct mrb_insn_data {
+  uint8_t insn;
+  uint16_t a;
+  uint16_t b;
+  uint8_t c;
+};
+
+struct mrb_insn_data mrb_decode_insn(mrb_code *pc);
 
 MRB_END_DECL
 
index 4f2134a..373e3be 100644 (file)
@@ -14,7 +14,7 @@
   struct RClass *c;\
   struct RBasic *gcnext
 
-#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag)
+#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & (flag))
 
 
 struct RBasic {
@@ -22,11 +22,10 @@ struct RBasic {
 };
 #define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
 
-/* flags bits >= 18 is reserved */
-#define MRB_FLAG_IS_FROZEN (1 << 18)
-#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)
+#define MRB_FL_OBJ_IS_FROZEN (1 << 20)
+#define MRB_FROZEN_P(o) ((o)->flags & MRB_FL_OBJ_IS_FROZEN)
+#define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FL_OBJ_IS_FROZEN)
+#define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FL_OBJ_IS_FROZEN)
 
 struct RObject {
   MRB_OBJECT_HEADER;
index 9a51162..d513ca4 100644 (file)
 #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                         */
+enum mrb_insn {
+#define OPCODE(x,_) OP_ ## x,
+#include "mruby/ops.h"
+#undef OPCODE
 };
 
 #define OP_L_STRICT  1
@@ -158,4 +23,47 @@ enum {
 #define OP_R_BREAK  1
 #define OP_R_RETURN 2
 
+#define PEEK_B(pc) (*(pc))
+#define PEEK_S(pc) ((pc)[0]<<8|(pc)[1])
+#define PEEK_W(pc) ((pc)[0]<<16|(pc)[1]<<8|(pc)[2])
+
+#define READ_B() PEEK_B(pc++)
+#define READ_S() (pc+=2, PEEK_S(pc-2))
+#define READ_W() (pc+=3, PEEK_W(pc-3))
+
+#define FETCH_Z() /* nothing */
+#define FETCH_B() do {a=READ_B();} while (0)
+#define FETCH_BB() do {a=READ_B(); b=READ_B();} while (0)
+#define FETCH_BBB() do {a=READ_B(); b=READ_B(); c=READ_B();} while (0)
+#define FETCH_BS() do {a=READ_B(); b=READ_S();} while (0)
+#define FETCH_S() do {a=READ_S();} while (0)
+#define FETCH_W() do {a=READ_W();} while (0)
+
+/* with OP_EXT1 (1st 16bit) */
+#define FETCH_Z_1() FETCH_Z()
+#define FETCH_B_1() FETCH_S()
+#define FETCH_BB_1() do {a=READ_S(); b=READ_B();} while (0)
+#define FETCH_BBB_1() do {a=READ_S(); b=READ_B(); c=READ_B();} while (0)
+#define FETCH_BS_1() do {a=READ_S(); b=READ_S();} while (0)
+#define FETCH_S_1() FETCH_S()
+#define FETCH_W_1() FETCH_W()
+
+/* with OP_EXT2 (2nd 16bit) */
+#define FETCH_Z_2() FETCH_Z()
+#define FETCH_B_2() FETCH_B()
+#define FETCH_BB_2() do {a=READ_B(); b=READ_S();} while (0)
+#define FETCH_BBB_2() do {a=READ_B(); b=READ_S(); c=READ_B();} while (0)
+#define FETCH_BS_2() FETCH_BS()
+#define FETCH_S_2() FETCH_S()
+#define FETCH_W_2() FETCH_W()
+
+/* with OP_EXT3 (1st & 2nd 16bit) */
+#define FETCH_Z_3() FETCH_Z()
+#define FETCH_B_3() FETCH_B()
+#define FETCH_BB_3() do {a=READ_S(); b=READ_S();} while (0)
+#define FETCH_BBB_3() do {a=READ_S(); b=READ_S(); c=READ_B();} while (0)
+#define FETCH_BS_3() do {a=READ_S(); b=READ_S();} while (0)
+#define FETCH_S_3() FETCH_S()
+#define FETCH_W_3() FETCH_W()
+
 #endif  /* MRUBY_OPCODE_H */
diff --git a/third-party/mruby/include/mruby/ops.h b/third-party/mruby/include/mruby/ops.h
new file mode 100644 (file)
index 0000000..d645946
--- /dev/null
@@ -0,0 +1,117 @@
+/* operand types:
+   + Z: no operand (Z,Z,Z,Z)
+   + B: 8bit (B,S,B,B)
+   + BB: 8+8bit (BB,SB,BS,SS)
+   + BBB: 8+8+8bit (BBB,SBB,BSB,SSB)
+   + BS: 8+16bit (BS,SS,BS,BS)
+   + S: 16bit (S,S,S,S)
+   + W: 24bit (W,W,W,W)
+*/
+
+/*-----------------------------------------------------------------------
+operation code    operands      semantics
+------------------------------------------------------------------------*/
+OPCODE(NOP,        Z)        /* no operation */
+OPCODE(MOVE,       BB)       /* R(a) = R(b) */
+OPCODE(LOADL,      BB)       /* R(a) = Pool(b) */
+OPCODE(LOADI,      BB)       /* R(a) = mrb_int(b) */
+OPCODE(LOADINEG,   BB)       /* R(a) = mrb_int(-b) */
+OPCODE(LOADI__1,   B)        /* R(a) = mrb_int(-1) */
+OPCODE(LOADI_0,    B)        /* R(a) = mrb_int(0) */
+OPCODE(LOADI_1,    B)        /* R(a) = mrb_int(1) */
+OPCODE(LOADI_2,    B)        /* R(a) = mrb_int(2) */
+OPCODE(LOADI_3,    B)        /* R(a) = mrb_int(3) */
+OPCODE(LOADI_4,    B)        /* R(a) = mrb_int(4) */
+OPCODE(LOADI_5,    B)        /* R(a) = mrb_int(5) */
+OPCODE(LOADI_6,    B)        /* R(a) = mrb_int(6) */
+OPCODE(LOADI_7,    B)        /* R(a) = mrb_int(7) */
+OPCODE(LOADSYM,    BB)       /* R(a) = Syms(b) */
+OPCODE(LOADNIL,    B)        /* R(a) = nil */
+OPCODE(LOADSELF,   B)        /* R(a) = self */
+OPCODE(LOADT,      B)        /* R(a) = true */
+OPCODE(LOADF,      B)        /* R(a) = false */
+OPCODE(GETGV,      BB)       /* R(a) = getglobal(Syms(b)) */
+OPCODE(SETGV,      BB)       /* setglobal(Syms(b), R(a)) */
+OPCODE(GETSV,      BB)       /* R(a) = Special[Syms(b)] */
+OPCODE(SETSV,      BB)       /* Special[Syms(b)] = R(a) */
+OPCODE(GETIV,      BB)       /* R(a) = ivget(Syms(b)) */
+OPCODE(SETIV,      BB)       /* ivset(Syms(b),R(a)) */
+OPCODE(GETCV,      BB)       /* R(a) = cvget(Syms(b)) */
+OPCODE(SETCV,      BB)       /* cvset(Syms(b),R(a)) */
+OPCODE(GETCONST,   BB)       /* R(a) = constget(Syms(b)) */
+OPCODE(SETCONST,   BB)       /* constset(Syms(b),R(a)) */
+OPCODE(GETMCNST,   BB)       /* R(a) = R(a)::Syms(b) */
+OPCODE(SETMCNST,   BB)       /* R(a+1)::Syms(b) = R(a) */
+OPCODE(GETUPVAR,   BBB)      /* R(a) = uvget(b,c) */
+OPCODE(SETUPVAR,   BBB)      /* uvset(b,c,R(a)) */
+OPCODE(JMP,        S)        /* pc=a */
+OPCODE(JMPIF,      BS)       /* if R(b) pc=a */
+OPCODE(JMPNOT,     BS)       /* if !R(b) pc=a */
+OPCODE(JMPNIL,     BS)       /* if R(b)==nil pc=a */
+OPCODE(ONERR,      S)        /* rescue_push(a) */
+OPCODE(EXCEPT,     B)        /* R(a) = exc */
+OPCODE(RESCUE,     BB)       /* R(b) = R(a).isa?(R(b)) */
+OPCODE(POPERR,     B)        /* a.times{rescue_pop()} */
+OPCODE(RAISE,      B)        /* raise(R(a)) */
+OPCODE(EPUSH,      B)        /* ensure_push(SEQ[a]) */
+OPCODE(EPOP,       B)        /* A.times{ensure_pop().call} */
+OPCODE(SENDV,      BB)       /* R(a) = call(R(a),Syms(b),*R(a+1)) */
+OPCODE(SENDVB,     BB)       /* R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2)) */
+OPCODE(SEND,       BBB)      /* R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c)) */
+OPCODE(SENDB,      BBB)      /* R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) */
+OPCODE(CALL,       Z)        /* R(0) = self.call(frame.argc, frame.argv) */
+OPCODE(SUPER,      BB)       /* R(a) = super(R(a+1),... ,R(a+b+1)) */
+OPCODE(ARGARY,     BS)       /* R(a) = argument array (16=m5:r1:m5:d1:lv4) */
+OPCODE(ENTER,      W)        /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */
+OPCODE(KEY_P,      BB)       /* R(a) = kdict.key?(Syms(b))                      # todo */
+OPCODE(KEYEND,     Z)        /* raise unless kdict.empty?                       # todo */
+OPCODE(KARG,       BB)       /* R(a) = kdict[Syms(b)]; kdict.delete(Syms(b))    # todo */
+OPCODE(RETURN,     B)        /* return R(a) (normal) */
+OPCODE(RETURN_BLK, B)        /* return R(a) (in-block return) */
+OPCODE(BREAK,      B)        /* break R(a) */
+OPCODE(BLKPUSH,    BS)       /* R(a) = block (16=m5:r1:m5:d1:lv4) */
+OPCODE(ADD,        B)        /* R(a) = R(a)+R(a+1) */
+OPCODE(ADDI,       BB)       /* R(a) = R(a)+mrb_int(c)  */
+OPCODE(SUB,        B)        /* R(a) = R(a)-R(a+1) */
+OPCODE(SUBI,       BB)       /* R(a) = R(a)-C */
+OPCODE(MUL,        B)        /* R(a) = R(a)*R(a+1) */
+OPCODE(DIV,        B)        /* R(a) = R(a)/R(a+1) */
+OPCODE(EQ,         B)        /* R(a) = R(a)==R(a+1) */
+OPCODE(LT,         B)        /* R(a) = R(a)<R(a+1) */
+OPCODE(LE,         B)        /* R(a) = R(a)<=R(a+1) */
+OPCODE(GT,         B)        /* R(a) = R(a)>R(a+1) */
+OPCODE(GE,         B)        /* R(a) = R(a)>=R(a+1) */
+OPCODE(ARRAY,      BB)       /* R(a) = ary_new(R(a),R(a+1)..R(a+b)) */
+OPCODE(ARRAY2,     BBB)      /* R(a) = ary_new(R(b),R(b+1)..R(b+c)) */
+OPCODE(ARYCAT,     B)        /* ary_cat(R(a),R(a+1)) */
+OPCODE(ARYPUSH,    B)        /* ary_push(R(a),R(a+1)) */
+OPCODE(ARYDUP,     B)        /* R(a) = ary_dup(R(a)) */
+OPCODE(AREF,       BBB)      /* R(a) = R(b)[c] */
+OPCODE(ASET,       BBB)      /* R(a)[c] = R(b) */
+OPCODE(APOST,      BBB)      /* *R(a),R(a+1)..R(a+c) = R(a)[b..] */
+OPCODE(INTERN,     B)        /* R(a) = intern(R(a)) */
+OPCODE(STRING,     BB)       /* R(a) = str_dup(Lit(b)) */
+OPCODE(STRCAT,     B)        /* str_cat(R(a),R(a+1)) */
+OPCODE(HASH,       BB)       /* R(a) = hash_new(R(a),R(a+1)..R(a+b)) */
+OPCODE(HASHADD,    BB)       /* R(a) = hash_push(R(a),R(a+1)..R(a+b)) */
+OPCODE(HASHCAT,    B)        /* R(a) = hash_cat(R(a),R(a+1)) */
+OPCODE(LAMBDA,     BB)       /* R(a) = lambda(SEQ[b],L_LAMBDA) */
+OPCODE(BLOCK,      BB)       /* R(a) = lambda(SEQ[b],L_BLOCK) */
+OPCODE(METHOD,     BB)       /* R(a) = lambda(SEQ[b],L_METHOD) */
+OPCODE(RANGE_INC,  B)        /* R(a) = range_new(R(a),R(a+1),FALSE) */
+OPCODE(RANGE_EXC,  B)        /* R(a) = range_new(R(a),R(a+1),TRUE) */
+OPCODE(OCLASS,     B)        /* R(a) = ::Object */
+OPCODE(CLASS,      BB)       /* R(a) = newclass(R(a),Syms(b),R(a+1)) */
+OPCODE(MODULE,     BB)       /* R(a) = newmodule(R(a),Syms(b)) */
+OPCODE(EXEC,       BB)       /* R(a) = blockexec(R(a),SEQ[b]) */
+OPCODE(DEF,        BB)       /* R(a).newmethod(Syms(b),R(a+1)) */
+OPCODE(ALIAS,      BB)       /* alias_method(target_class,Syms(a),Syms(b)) */
+OPCODE(UNDEF,      B)        /* undef_method(target_class,Syms(a)) */
+OPCODE(SCLASS,     B)        /* R(a) = R(a).singleton_class */
+OPCODE(TCLASS,     B)        /* R(a) = target_class */
+OPCODE(DEBUG,      BBB)      /* print a,b,c */
+OPCODE(ERR,        B)        /* raise(LocalJumpError, Lit(a)) */
+OPCODE(EXT1,       Z)        /* make 1st operand 16bit */
+OPCODE(EXT2,       Z)        /* make 2nd operand 16bit */
+OPCODE(EXT3,       Z)        /* make 1st and 2nd operands 16bit */
+OPCODE(STOP,       Z)        /* stop VM */
index aa281b6..021f9c1 100644 (file)
@@ -23,13 +23,13 @@ struct REnv {
 };
 
 /* flags (21bits): 1(shared flag):10(cioff/bidx):10(stack_len) */
-#define MRB_ENV_SET_STACK_LEN(e,len) (e)->flags = (((e)->flags & ~0x3ff)|((unsigned int)(len) & 0x3ff))
+#define MRB_ENV_SET_STACK_LEN(e,len) ((e)->flags = (((e)->flags & ~0x3ff)|((unsigned int)(len) & 0x3ff)))
 #define MRB_ENV_STACK_LEN(e) ((mrb_int)((e)->flags & 0x3ff))
 #define MRB_ENV_STACK_UNSHARED (1<<20)
-#define MRB_ENV_UNSHARE_STACK(e) (e)->flags |= MRB_ENV_STACK_UNSHARED
+#define MRB_ENV_UNSHARE_STACK(e) ((e)->flags |= MRB_ENV_STACK_UNSHARED)
 #define MRB_ENV_STACK_SHARED_P(e) (((e)->flags & MRB_ENV_STACK_UNSHARED) == 0)
 #define MRB_ENV_BIDX(e) (((e)->flags >> 10) & 0x3ff)
-#define MRB_ENV_SET_BIDX(e,idx) (e)->flags = (((e)->flags & ~(0x3ff<<10))|((unsigned int)(idx) & 0x3ff)<<10)
+#define MRB_ENV_SET_BIDX(e,idx) ((e)->flags = (((e)->flags & ~(0x3ff<<10))|((unsigned int)(idx) & 0x3ff)<<10))
 
 void mrb_env_unshare(mrb_state*, struct REnv*);
 
@@ -69,11 +69,11 @@ struct RProc {
 #define MRB_PROC_SET_TARGET_CLASS(p,tc) do {\
   if (MRB_PROC_ENV_P(p)) {\
     (p)->e.env->c = (tc);\
-    mrb_field_write_barrier(mrb, (struct RBasic*)(p)->e.env, (struct RBasic*)tc);\
+    mrb_field_write_barrier(mrb, (struct RBasic*)(p)->e.env, (struct RBasic*)(tc));\
   }\
   else {\
     (p)->e.target_class = (tc);\
-    mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)tc);\
+    mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)(tc));\
   }\
 } while (0)
 #define MRB_PROC_SCOPE 2048
@@ -88,7 +88,7 @@ MRB_API struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int
 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);
+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*);
@@ -101,8 +101,8 @@ MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int);
 #define MRB_METHOD_FUNC_FL ((uintptr_t)1)
 #define MRB_METHOD_FUNC_P(m) (((uintptr_t)(m))&MRB_METHOD_FUNC_FL)
 #define MRB_METHOD_FUNC(m) ((mrb_func_t)((uintptr_t)(m)&(~MRB_METHOD_FUNC_FL)))
-#define MRB_METHOD_FROM_FUNC(m,fn) m=(mrb_method_t)((struct RProc*)((uintptr_t)(fn)|MRB_METHOD_FUNC_FL))
-#define MRB_METHOD_FROM_PROC(m,pr) m=(mrb_method_t)(struct RProc*)(pr)
+#define MRB_METHOD_FROM_FUNC(m,fn) ((m)=(mrb_method_t)((struct RProc*)((uintptr_t)(fn)|MRB_METHOD_FUNC_FL)))
+#define MRB_METHOD_FROM_PROC(m,pr) ((m)=(mrb_method_t)(struct RProc*)(pr))
 #define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m))
 #define MRB_METHOD_PROC(m) ((struct RProc*)(m))
 #define MRB_METHOD_UNDEF_P(m) ((m)==0)
index b166e58..b562699 100644 (file)
  */
 MRB_BEGIN_DECL
 
+#if defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING)
+# define MRB_RANGE_EMBED
+#endif
+
+#ifdef MRB_RANGE_EMBED
+struct RRange {
+  MRB_OBJECT_HEADER;
+  mrb_value beg;
+  mrb_value end;
+  mrb_bool excl;
+};
+# define mrb_gc_free_range(mrb, p) ((void)0)
+# define RANGE_BEG(p) ((p)->beg)
+# define RANGE_END(p) ((p)->end)
+#else
 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_bool excl;
 };
+# define mrb_gc_free_range(mrb, p) mrb_free(mrb, (p)->edges)
+# define RANGE_BEG(p) ((p)->edges->beg)
+# define RANGE_END(p) ((p)->edges->end)
+#endif
+
+#define mrb_range_beg(mrb, r) RANGE_BEG(mrb_range_ptr(mrb, r))
+#define mrb_range_end(mrb, r) RANGE_END(mrb_range_ptr(mrb, r))
+#define mrb_range_excl_p(mrb, r) RANGE_EXCL(mrb_range_ptr(mrb, r))
+#define mrb_range_raw_ptr(r) ((struct RRange*)mrb_ptr(r))
+#define mrb_range_value(p) mrb_obj_value((void*)(p))
+#define RANGE_EXCL(p) ((p)->excl)
 
-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))
+MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value range);
 
 /*
  * Initializes a Range.
@@ -43,6 +66,7 @@ MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end,
 
 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));
+void mrb_gc_mark_range(mrb_state *mrb, struct RRange *r);
 
 MRB_END_DECL
 
index 975b1fe..0b90deb 100644 (file)
@@ -39,15 +39,15 @@ struct RString {
 #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;\
+  (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);\
+    (s)->as.heap.len = (mrb_int)(n);\
   }\
 } while (0)
 #define RSTR_EMBED_LEN(s)\
@@ -102,7 +102,7 @@ MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_i
 #define mrb_str_index_lit(mrb, str, lit, off) mrb_str_index(mrb, str, lit, mrb_strlen_lit(lit), off);
 
 /*
- * Appends self to other. Returns self as a concatnated string.
+ * Appends self to other. Returns self as a concatenated string.
  *
  *
  *  Example:
@@ -126,10 +126,10 @@ MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_i
  *       str1 = mrb_str_new_lit(mrb, "abc");
  *       str2 = mrb_str_new_lit(mrb, "def");
  *
- *       // Concatnates str2 to str1.
+ *       // Concatenates str2 to str1.
  *       mrb_str_concat(mrb, str1, str2);
  *
- *      // Prints new Concatnated Ruby string.
+ *      // Prints new Concatenated Ruby string.
  *      mrb_p(mrb, str1);
  *
  *      mrb_close(mrb);
@@ -178,10 +178,10 @@ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
  *       mrb_p(mrb, a);
  *       mrb_p(mrb, b);
  *
- *       // Concatnates both Ruby strings.
+ *       // Concatenates both Ruby strings.
  *       c = mrb_str_plus(mrb, a, b);
  *
- *      // Prints new Concatnated Ruby string.
+ *      // Prints new Concatenated Ruby string.
  *      mrb_p(mrb, c);
  *
  *      mrb_close(mrb);
@@ -193,7 +193,7 @@ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
  *
  *     => "abc"  # First string
  *     => "def"  # Second string
- *     => "abcdef" # First & Second concatnated.
+ *     => "abcdef" # First & Second concatenated.
  *
  * @param [mrb_state] mrb The current mruby state.
  * @param [mrb_value] a First string to concatenate.
@@ -311,9 +311,12 @@ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb
  * @param [mrb_value] str Ruby string.
  * @return [mrb_value] A Ruby string.
  */
+MRB_API mrb_value mrb_ensure_string_type(mrb_state *mrb, mrb_value str);
+MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str);
+/* obsolete: use mrb_ensure_string_type() instead */
 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_new_capa(mrb_state *mrb, size_t capa);
 MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
 
@@ -349,10 +352,13 @@ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
 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 mrb_value mrb_cstr_to_inum(mrb_state *mrb, const char *s, mrb_int base, mrb_bool badcheck);
 MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck);
+MRB_API double mrb_cstr_to_dbl(mrb_state *mrb, const char *s, mrb_bool badcheck);
 
 /*
  * Returns a converted string type.
+ * For type checking, non converting `mrb_to_str` is recommended.
  */
 MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
 
@@ -435,6 +441,10 @@ void mrb_regexp_check(mrb_state *mrb, mrb_value obj);
 #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)
 
+#ifdef MRB_UTF8_STRING
+mrb_int mrb_utf8_len(const char *str, mrb_int byte_len);
+#endif
+
 MRB_END_DECL
 
 #endif  /* MRUBY_STRING_H */
index 5d3d214..4a1fd8d 100644 (file)
@@ -15,9 +15,9 @@
 
 #if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
 
-#define MRB_TRY(buf) do { try {
+#define MRB_TRY(buf) 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_END_EXC(buf)  }
 
 #define MRB_THROW(buf) throw((buf)->impl)
 typedef mrb_int mrb_jmpbuf_impl;
@@ -34,9 +34,9 @@ typedef mrb_int mrb_jmpbuf_impl;
 #define MRB_LONGJMP longjmp
 #endif
 
-#define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) {
+#define MRB_TRY(buf) if (MRB_SETJMP((buf)->impl) == 0) {
 #define MRB_CATCH(buf) } else {
-#define MRB_END_EXC(buf) } } while(0)
+#define MRB_END_EXC(buf) }
 
 #define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1);
 #define mrb_jmpbuf_impl jmp_buf
index f988826..1ed2085 100644 (file)
@@ -160,6 +160,10 @@ typedef void mrb_value;
 #ifndef mrb_bool
 #define mrb_bool(o)   (mrb_type(o) != MRB_TT_FALSE)
 #endif
+#if !defined(MRB_SYMBOL_BITSIZE)
+#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT)
+#define MRB_SYMBOL_MAX      UINT32_MAX
+#endif
 #ifndef MRB_WITHOUT_FLOAT
 #define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT)
 #endif
index 5fef83f..ba60379 100644 (file)
@@ -31,8 +31,6 @@ struct global_entry {
 
 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);
@@ -42,8 +40,8 @@ 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_bool mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym sym);
+MRB_API void mrb_iv_name_sym_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);
@@ -125,6 +123,7 @@ 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_bool mrb_ident_p(const char *s, mrb_int len);
 
 /* GC functions */
 void mrb_gc_mark_gv(mrb_state*);
@@ -133,6 +132,10 @@ 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*);
 
+/* return non zero to break the loop */
+typedef int (mrb_iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
+MRB_API void mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p);
+
 MRB_END_DECL
 
 #endif  /* MRUBY_VARIABLE_H */
index 43d6461..e776373 100644 (file)
@@ -27,7 +27,7 @@ MRB_BEGIN_DECL
 /*
  * The version of Ruby used by mruby.
  */
-#define MRUBY_RUBY_VERSION "1.9"
+#define MRUBY_RUBY_VERSION "2.0"
 
 /*
  * Ruby engine.
@@ -37,12 +37,12 @@ MRB_BEGIN_DECL
 /*
  * Major release version number.
  */
-#define MRUBY_RELEASE_MAJOR 1
+#define MRUBY_RELEASE_MAJOR 2
 
 /*
  * Minor release version number.
  */
-#define MRUBY_RELEASE_MINOR 4
+#define MRUBY_RELEASE_MINOR 0
 
 /*
  * Tiny release version number.
@@ -62,7 +62,7 @@ MRB_BEGIN_DECL
 /*
  * Release year.
  */
-#define MRUBY_RELEASE_YEAR 2018
+#define MRUBY_RELEASE_YEAR 2019
 
 /*
  * Release month.
@@ -72,7 +72,7 @@ MRB_BEGIN_DECL
 /*
  * Release day.
  */
-#define MRUBY_RELEASE_DAY 27
+#define MRUBY_RELEASE_DAY 4
 
 /*
  * Release date as a string.
index 4c6d3ca..08e6f61 100644 (file)
@@ -18,18 +18,20 @@ class String
   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)
+  unless (sprintf("%{a}", :a => 1) rescue false)
+    def %(params)
+      if params.is_a?(Hash)
+        str = self.clone
+        params.each do |k, v|
+          str.gsub!("%{#{k}}") { v }
+        end
+        str
       else
-        sprintf(self, params)
+        if params.is_a?(Array)
+          sprintf(self, *params)
+        else
+          sprintf(self, params)
+        end
       end
     end
   end
@@ -37,17 +39,21 @@ end
 
 class Symbol
   # Compatible with 1.9 on 1.8
-  def to_proc
-    proc { |obj, *args| obj.send(self, *args) }
+  unless method_defined?(:to_proc)
+    def to_proc
+      proc { |obj, *args| obj.send(self, *args) }
+    end
   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
+  unless method_defined?(:each_with_object)
+    def each_with_object(memo)
+      return to_enum :each_with_object, memo unless block_given?
+      each { |obj| yield obj, memo }
+      memo
+    end
   end
 end
 
index 57bd9c5..016b32b 100644 (file)
@@ -45,9 +45,11 @@ module MRuby
     include Rake::DSL
     include LoadGems
     attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir
-    attr_reader :libmruby, :gems, :toolchains
+    attr_reader :libmruby_objs, :gems, :toolchains
     attr_writer :enable_bintest, :enable_test
 
+    alias libmruby libmruby_objs
+
     COMPILERS = %w(cc cxx objc asm)
     COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc)
     attr_block MRuby::Build::COMMANDS
@@ -81,7 +83,7 @@ module MRuby
         @mrbc = Command::Mrbc.new(self)
 
         @bins = []
-        @gems, @libmruby = MRuby::Gem::List.new, []
+        @gems, @libmruby_objs = MRuby::Gem::List.new, []
         @build_mrbtest_lib_only = false
         @cxx_exception_enabled = false
         @cxx_exception_disabled = false
@@ -100,6 +102,10 @@ module MRuby
       build_mrbtest if test_enabled?
     end
 
+    def debug_enabled?
+      @enable_debug
+    end
+
     def enable_debug
       compilers.each do |c|
         c.defines += %w(MRB_DEBUG)
@@ -108,6 +114,8 @@ module MRuby
         end
       end
       @mrbc.compile_options += ' -g'
+
+      @enable_debug = true
     end
 
     def disable_cxx_exception
@@ -154,8 +162,6 @@ module MRuby
     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|
@@ -167,7 +173,7 @@ module MRuby
 #ifndef MRB_ENABLE_CXX_ABI
 extern "C" {
 #endif
-#include "#{src}"
+#include "#{File.absolute_path src}"
 #ifndef MRB_ENABLE_CXX_ABI
 }
 #endif
@@ -264,8 +270,11 @@ EOS
     def exefile(name)
       if name.is_a?(Array)
         name.flatten.map { |n| exefile(n) }
-      else
+      elsif File.extname(name).empty?
         "#{name}#{exts.executable}"
+      else
+        # `name` sometimes have (non-standard) extension (e.g. `.bat`).
+        name
       end
     end
 
@@ -293,18 +302,22 @@ EOS
       @build_mrbtest_lib_only
     end
 
+    def verbose_flag
+      $verbose ? ' -v' : ''
+    end
+
     def run_test
       puts ">>> Test #{name} <<<"
       mrbtest = exefile("#{build_dir}/bin/mrbtest")
-      sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}"
+      sh "#{filename mrbtest.relative_path}#{verbose_flag}"
       puts
-      run_bintest if bintest_enabled?
     end
 
     def run_bintest
+      puts ">>> Bintest #{name} <<<"
       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 ' '}"
+      sh "ruby test/bintest.rb#{verbose_flag} #{targets.join ' '}"
     end
 
     def print_build_summary
@@ -324,6 +337,18 @@ EOS
       puts "================================================"
       puts
     end
+
+    def libmruby_static
+      libfile("#{build_dir}/lib/libmruby")
+    end
+
+    def libmruby_core_static
+      libfile("#{build_dir}/lib/libmruby_core")
+    end
+
+    def libraries
+      [libmruby_static]
+    end
   end # Build
 
   class CrossBuild < Build
index 694b4a2..0f18e0e 100644 (file)
@@ -67,8 +67,8 @@ module MRuby
       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 }
+    def all_flags(_defines=[], _include_paths=[], _flags=[])
+      define_flags = [defines, _defines].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)
@@ -79,14 +79,14 @@ module MRuby
       [flags, define_flags, include_path_flags, _flags].flatten.join(' ')
     end
 
-    def run(outfile, infile, _defineds=[], _include_paths=[], _flags=[])
+    def run(outfile, infile, _defines=[], _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),
+        _run compile_options, { :flags => all_flags(_defines, _include_paths, _flags),
                                 :infile => cygwin_filename(infile), :outfile => cygwin_filename(outfile) }
       else
-        _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags),
+        _run compile_options, { :flags => all_flags(_defines, _include_paths, _flags),
                                 :infile => filename(infile), :outfile => filename(outfile) }
       end
     end
index a83ca63..ce2e01a 100644 (file)
@@ -52,6 +52,8 @@ module MRuby
       end
 
       def setup
+        return if defined?(@linker)  # return if already set up
+
         MRuby::Gem.current = self
         MRuby::Build::COMMANDS.each do |command|
           instance_variable_set("@#{command}", @build.send(command).clone)
@@ -63,7 +65,7 @@ module MRuby
           objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
         end
 
-        @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb")
+        @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb").sort
         @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
@@ -87,7 +89,7 @@ module MRuby
           fail "#{name || dir} required to set name, license(s) and author(s)"
         end
 
-        build.libmruby << @objs
+        build.libmruby_objs << @objs
 
         instance_eval(&@build_config_initializer) if @build_config_initializer
       end
@@ -110,17 +112,13 @@ module MRuby
       end
 
       def add_test_dependency(*args)
-        add_dependency(*args) if build.test_enabled?
+        add_dependency(*args) if build.test_enabled? || build.bintest_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
@@ -334,26 +332,26 @@ module MRuby
       end
 
       def generate_gem_table build
-        gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
+        gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec }
 
-        default_gems = []
+        default_gems = {}
         each do |g|
           g.dependencies.each do |dep|
-            default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
+            default_gems[dep[:gem]] ||= default_gem_params(dep)
           end
         end
 
         until default_gems.empty?
-          def_gem = default_gems.pop
+          def_name, def_gem = default_gems.shift
+          next if gem_table[def_name]
 
-          spec = build.gem def_gem[:default]
-          fail "Invalid gem name: #{spec.name} (Expected: #{def_gem[:gem]})" if spec.name != def_gem[:gem]
+          spec = gem_table[def_name] = build.gem(def_gem[:default])
+          fail "Invalid gem name: #{spec.name} (Expected: #{def_name})" if spec.name != def_name
           spec.setup
 
           spec.dependencies.each do |dep|
-            default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
+            default_gems[dep[:gem]] ||= default_gem_params(dep)
           end
-          gem_table[spec.name] = spec
         end
 
         each do |g|
@@ -428,7 +426,8 @@ module MRuby
       end
 
       def import_include_paths(g)
-        gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
+        gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec }
+
         g.dependencies.each do |dep|
           dep_g = gem_table[dep[:gem]]
           # We can do recursive call safely
@@ -454,5 +453,6 @@ module MRuby
     def new(&block); block.call(self); end
     def config=(obj); @config = obj; end
     def gem(gemdir, &block); @config.gem(gemdir, &block); end
+    def gembox(gemfile); @config.gembox(gemfile); end
   end # GemBox
 end # MRuby
index 542c37a..cd2d858 100755 (executable)
@@ -7,6 +7,10 @@
 require 'getoptlong'
 require 'fileutils'
 
+$rake_fiber_table = {}
+$rake_jobs = 1
+$rake_failed = []
+
 class String
   def ext(newext='')
     return self.dup if ['.', '..'].include? self
@@ -86,14 +90,36 @@ module MiniRake
       @name.to_s
     end
 
-    # Invoke the task if it is needed.  Prerequites are invoked first.
+    def done?; @done end
+    def running?; @running end
+
+    # Invoke the task if it is needed. Prerequisites 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?
+      prerequisites.each do |n|
+        t = Task[n]
+        unless t.done?
+          return prerequisites.select{|v| v = Task[v]; v && (!v.done? || !v.running?) }
+        end
+      end
+
+      @already_invoked = true
+
+      if needed?
+        @running = true
+        if $rake_root_fiber
+          return Fiber.new do
+            self.execute
+            $rake_root_fiber.transfer
+          end
+        else
+          self.execute
+        end
+      end
+
+      @done = true
     end
 
     # Execute the actions associated with this task.
@@ -103,6 +129,8 @@ module MiniRake
       unless $dryrun
         @actions.each { |act| act.call(self) }
       end
+      @done = true
+      @running = false
     end
 
     # Is this task needed?
@@ -281,7 +309,19 @@ module MiniRake
     # Run the system command +cmd+.
     def sh(cmd)
       puts cmd if $verbose
-      system(cmd) or fail "Command Failed: [#{cmd}]"
+
+      if !$rake_root_fiber || Fiber.current == $rake_root_fiber
+        system(cmd) or fail "Command Failed: [#{cmd}]"
+        return
+      end
+
+      pid = Process.spawn(cmd)
+      $rake_fiber_table[pid] = {
+        fiber: Fiber.current,
+        command: cmd,
+        process_waiter: Process.detach(pid)
+      }
+      $rake_root_fiber.transfer
     end
 
     def desc(text)
@@ -329,7 +369,9 @@ class RakeApp
     ['--verbose',  '-v', GetoptLong::NO_ARGUMENT,
       "Log message to standard output."],
     ['--directory', '-C', GetoptLong::REQUIRED_ARGUMENT,
-      "Change executing directory of rakefiles."]
+      "Change executing directory of rakefiles."],
+    ['--jobs', '-j', GetoptLong::REQUIRED_ARGUMENT,
+      'Execute rake with parallel jobs.']
   ]
 
   # Create a RakeApp object.
@@ -422,6 +464,8 @@ class RakeApp
       exit
     when '--directory'
       Dir.chdir value
+    when '--jobs'
+      $rake_jobs = [value.to_i, 1].max
     else
       fail "Unknown option: #{opt}"
     end
@@ -438,6 +482,12 @@ class RakeApp
   # Run the +rake+ application.
   def run
     handle_options
+
+    unless $rake_root_fiber
+      require 'fiber'
+      $rake_root_fiber = Fiber.current
+    end
+
     begin
       here = Dir.pwd
       while ! have_rakefile
@@ -447,12 +497,12 @@ class RakeApp
         end
         here = Dir.pwd
       end
-      tasks = []
+      root_tasks = []
       ARGV.each do |task_name|
         if /^(\w+)=(.*)/.match(task_name)
           ENV[$1] = $2
         else
-          tasks << task_name
+          root_tasks << task_name
         end
       end
       puts "(in #{Dir.pwd})"
@@ -461,22 +511,93 @@ class RakeApp
       if $show_tasks
         display_tasks
       else
-        tasks.push("default") if tasks.size == 0
-        tasks.each do |task_name|
-          MiniRake::Task[task_name].invoke
+        root_tasks.push("default") if root_tasks.empty?
+        # revese tasks for popping
+        root_tasks.reverse!
+
+        tasks = []
+        until root_tasks.empty?
+          root_name = root_tasks.pop
+          tasks << root_name
+          until tasks.empty?
+            task_name = tasks.pop
+            t = MiniRake::Task[task_name]
+            f = t.invoke
+
+            # append additional tasks to task queue
+            if f.kind_of?(Array)
+              tasks.push(*f)
+              tasks.uniq!
+            end
+
+            unless f.kind_of? Fiber
+              tasks.insert 0, task_name unless t.done?
+              if root_name == task_name
+                wait_process
+              end
+              next
+            end
+
+            wait_process while $rake_fiber_table.size >= $rake_jobs
+
+            f.transfer
+          end
         end
+
+        wait_process until $rake_fiber_table.empty?
+      end
+    rescue Exception => e
+      begin
+        $rake_failed << e
+        wait_process until $rake_fiber_table.empty?
+      rescue Exception => next_e
+        e = next_e
+        retry
       end
-    rescue Exception => ex
-      puts "rake aborted!"
+    end
+
+    return if $rake_failed.empty?
+
+    puts "rake aborted!"
+    $rake_failed.each do |ex|
       puts ex.message
-      if $trace
+      if $trace || $verbose
         puts ex.backtrace.join("\n")
       else
         puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
       end
-      exit 1
     end
+    exit 1
   end
+
+  def wait_process(count = 0)
+    dur = [0.0001 * (10 ** count), 1].min
+    sleep dur
+
+    exited = []
+    $rake_fiber_table.each do |pid, v|
+      exited << pid unless v[:process_waiter].alive?
+    end
+
+    exited.each do |pid|
+      ent = $rake_fiber_table.delete pid
+      st = ent[:process_waiter].value
+
+      # ignore process that isn't created by `sh` method
+      return if ent.nil?
+
+      if st.exitstatus != 0
+        raise "Command Failed: [#{ent[:command]}]"
+      end
+
+      fail 'task scheduling bug!' if $rake_fiber_table.size >= $rake_jobs
+
+      ent[:fiber].transfer
+    end
+
+    wait_process(count + 1) if !$rake_fiber_table.empty? && exited.empty?
+  end
+
 end
 
 if __FILE__ == $0 then
index 7ddbb16..23e65fc 100644 (file)
@@ -1,4 +1,7 @@
 MRuby::GemBox.new do |conf|
+  # Meta-programming features
+  conf.gem :core => "mruby-metaprog"
+
   # Use standard IO/File class
   conf.gem :core => "mruby-io"
 
index c0995bb..387bd6c 100644 (file)
@@ -1,31 +1,6 @@
 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
   #
@@ -41,23 +16,19 @@ class Array
   #    c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
   #
   def uniq!(&block)
-    ary = self.dup
-    result = []
+    hash = {}
     if block
-      hash = {}
-      while ary.size > 0
-        val = ary.shift
+      self.each do |val|
         key = block.call(val)
-        hash[key] = val unless hash.has_key?(key)
-      end
-      hash.each_value do |value|
-        result << value
+        hash[key] = val unless hash.key?(key)
       end
+      result = hash.values
     else
-      while ary.size > 0
-        result << ary.shift
-        ary.delete(result.last)
+      hash = {}
+      self.each do |val|
+        hash[val] = val
       end
+      result = hash.keys
     end
     if result.size == self.size
       nil
@@ -136,6 +107,25 @@ class Array
 
   ##
   # call-seq:
+  #    ary.union(other_ary,...)  -> new_ary
+  #
+  # Set Union---Returns a new array by joining this array with
+  # <i>other_ary</i>, removing duplicates.
+  #
+  #    ["a", "b", "c"].union(["c", "d", "a"], ["a", "c", "e"])
+  #           #=> ["a", "b", "c", "d", "e"]
+  #
+  def union(*args)
+    ary = self.dup
+    args.each do |x|
+      ary.concat(x)
+      ary.uniq!
+    end
+    ary
+  end
+
+  ##
+  # call-seq:
   #    ary & other_ary      -> new_ary
   #
   # Set Intersection---Returns a new array
@@ -276,7 +266,6 @@ class Array
     self
   end
 
-  NONE=Object.new
   ##
   #  call-seq:
   #     ary.fetch(index)                    -> obj
@@ -301,7 +290,7 @@ class Array
   #                              #=> "100 is out of bounds"
   #
 
-  def fetch(n=nil, ifnone=NONE, &block)
+  def fetch(n, ifnone=NONE, &block)
     warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
 
     idx = n
@@ -783,16 +772,6 @@ class Array
   end
 
   ##
-  #  call-seq:
-  #     ary.to_ary -> ary
-  #
-  #  Returns +self+.
-  #
-  def to_ary
-    self
-  end
-
-  ##
   # call-seq:
   #   ary.dig(idx, ...)                 -> object
   #
@@ -911,7 +890,7 @@ class Array
   #
   # Assumes that self is an array of arrays and transposes the rows and columns.
   #
-  # If the length of the subarrays dont match, an IndexError is raised.
+  # If the length of the subarrays don't match, an IndexError is raised.
   #
   # Examples:
   #
@@ -932,4 +911,29 @@ class Array
       self.map { |row| row[column_index] }
     end
   end
+
+  ##
+  #  call-seq:
+  #    ary.to_h                ->   Hash
+  #    ary.to_h{|item| ... }   ->   Hash
+  #
+  # Returns the result of interpreting <i>aray</i> as an array of
+  # <tt>[key, value]</tt> pairs. If a block is given, it should
+  # return <tt>[key, value]</tt> pairs to construct a hash.
+  #
+  #     [[:foo, :bar], [1, 2]].to_h
+  #       # => {:foo => :bar, 1 => 2}
+  #     [1, 2].to_h{|x| [x, x*2]}
+  #       # => {1 => 2, 2 => 4}
+  #
+  def to_h(&blk)
+    h = {}
+    self.each do |v|
+      v = blk.call(v) if blk
+      raise TypeError, "wrong element type #{v.class}" unless Array === v
+      raise ArgumentError, "wrong array length (expected 2, was #{v.length})" unless v.length == 2
+      h[v[0]] = v[1]
+    end
+    h
+  end
 end
index 169f968..b6d9c9c 100644 (file)
@@ -106,49 +106,6 @@ mrb_ary_values_at(mrb_state *mrb, mrb_value self)
   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) {
-    mrb_value elt = RARRAY_PTR(ary)[i];
-    v = mrb_check_array_type(mrb, elt);
-
-    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, elt)),
-                 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:
@@ -175,7 +132,7 @@ 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_int i, j, k, len, alen;
   mrb_value val;
   mrb_value *ptr;
   mrb_value ary;
@@ -188,7 +145,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
     mrb_get_args(mrb, "o|i", &index, &len);
     switch (mrb_type(index)) {
     case MRB_TT_RANGE:
-      if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
+      if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == 1) {
         goto delete_pos_len;
       }
       else {
@@ -205,6 +162,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
 
   mrb_get_args(mrb, "ii", &i, &len);
  delete_pos_len:
+  alen = ARY_LEN(a);
   if (i < 0) i += alen;
   if (i < 0 || alen < i) return mrb_nil_value();
   if (len < 0) return mrb_nil_value();
@@ -236,7 +194,6 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
   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());
 }
 
index 7d810ac..853554b 100644 (file)
@@ -1,13 +1,6 @@
 ##
 # 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" ]
@@ -75,6 +68,14 @@ assert("Array#|") do
   assert_equal [1, 2, 3, 1], a
 end
 
+assert("Array#union") do
+  a = [1, 2, 3, 1]
+  b = [1, 4]
+  c = [1, 5]
+
+  assert_equal [1, 2, 3, 4, 5], a.union(b,c)
+end
+
 assert("Array#&") do
   a = [1, 2, 3, 1]
   b = [1, 4]
@@ -330,27 +331,11 @@ assert('Array#to_h') do
   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))
@@ -418,5 +403,5 @@ assert('Array#transpose') do
   assert_equal([[1], [2], [3]].transpose, [[1,2,3]])
   assert_equal([[1,2], [3,4], [5,6]].transpose, [[1,3,5], [2,4,6]])
   assert_raise(TypeError) { [1].transpose }
-  assert_raise(IndexError) { [[1], [2,3,4]].transpose } 
+  assert_raise(IndexError) { [[1], [2,3,4]].transpose }
 end
diff --git a/third-party/mruby/mrbgems/mruby-bin-config/mrbgem.rake b/third-party/mruby/mrbgems/mruby-bin-config/mrbgem.rake
new file mode 100644 (file)
index 0000000..3a0a1b8
--- /dev/null
@@ -0,0 +1,23 @@
+unless MRuby::Build.current.kind_of?(MRuby::CrossBuild)
+  MRuby::Gem::Specification.new('mruby-bin-config') do |spec|
+    name = 'mruby-config'
+    spec.license = 'MIT'
+    spec.author  = 'mruby developers'
+    spec.summary = "#{name} command"
+
+    mruby_config = name + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
+    mruby_config_path = "#{build.build_dir}/bin/#{mruby_config}"
+    make_cfg = "#{build.build_dir}/lib/libmruby.flags.mak"
+    tmplt_path = "#{__dir__}/#{mruby_config}"
+    build.bins << mruby_config
+
+    file mruby_config_path => [make_cfg, tmplt_path] do |t|
+      config = Hash[File.readlines(make_cfg).map!(&:chomp).map! {|l|
+        l.gsub('\\"', '"').split(' = ', 2).map! {|s| s.sub(/^(?=.)/, 'echo ')}
+      }]
+      tmplt = File.read(tmplt_path)
+      File.write(t.name, tmplt.gsub(/(#{Regexp.union(*config.keys)})\b/, config))
+      FileUtils.chmod(0755, t.name)
+    end
+  end
+end
index 0d4aad0..6675392 100644 (file)
@@ -317,7 +317,7 @@ TestConstNameSubClass.new.m()
 bp = nil
 SRC
 
-  # todo: wait for 'break' to be implimented
+  # todo: wait for 'break' to be implemented
   tc = []
   9.times { tc << {:cmd=>"s"} }
   tc << {:cmd=>"p CONST", :exp=>"super class"}
index d3ccf08..513db4d 100644 (file)
@@ -84,7 +84,7 @@ free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
 }
 
 static uint16_t
-check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
+check_file_lineno(mrb_state *mrb, struct mrb_irep *irep, const char *file, uint16_t lineno)
 {
   mrb_irep_debug_info_file *info_file;
   uint16_t result = 0;
@@ -93,8 +93,10 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
   uint16_t i;
 
   for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
+    const char *filename;
     info_file = irep->debug_info->files[f_idx];
-    if (!strcmp(info_file->filename, file)) {
+    filename = mrb_sym2name_len(mrb, info_file->filename_sym, NULL);
+    if (!strcmp(filename, file)) {
       result = MRB_DEBUG_BP_FILE_OK;
 
       fix_lineno = check_lineno(info_file, lineno);
@@ -103,7 +105,7 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
       }
     }
     for (i=0; i < irep->rlen; ++i) {
-      result  |= check_file_lineno(irep->reps[i], file, lineno);
+      result  |= check_file_lineno(mrb, irep->reps[i], file, lineno);
       if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
         return result;
       }
@@ -185,7 +187,7 @@ mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *fil
   }
 
   /* file and lineno check (line type mrb_debug_line_ary only.) */
-  result = check_file_lineno(dbg->root_irep, file, lineno);
+  result = check_file_lineno(mrb, dbg->root_irep, file, lineno);
   if (result == 0) {
     return MRB_DEBUG_BREAK_INVALID_FILE;
   }
@@ -426,10 +428,10 @@ mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
 }
 
 static mrb_bool
-check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
+check_start_pc_for_line(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, uint16_t line)
 {
   if (pc > irep->iseq) {
-    if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) {
+    if (line == mrb_debug_get_line(mrb, irep, pc - irep->iseq - 1)) {
       return FALSE;
     }
   }
@@ -447,7 +449,7 @@ mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const ch
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
-  if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
+  if (!check_start_pc_for_line(mrb, dbg->irep, dbg->pc, line)) {
     return MRB_DEBUG_OK;
   }
 
index 21fe641..66ddfa7 100644 (file)
@@ -181,7 +181,7 @@ mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, cons
   else srcname = filename;
 
   search_path[0] = srcpath;
-  search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0));
+  search_path[1] = dirname(mrb, mrb_debug_get_filename(mrb, mrdb->dbg->irep, 0));
   search_path[2] = ".";
 
   for (i = 0; i < 3; i++) {
index c870053..f888d14 100644 (file)
@@ -31,7 +31,7 @@ mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size
 }
 
 mrb_value
-mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc)
+mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc, int direct_eval)
 {
   void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
   mrb_value ruby_code;
@@ -48,6 +48,11 @@ mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t
     v = mrb_obj_value(mrb->exc);
     mrb->exc = 0;
   }
+  else if (direct_eval) {
+    recv = dbg->regs[0];
+
+    v = mrb_funcall(mrb, recv, expr, 0);
+  }
   else {
     /*
      * begin
index e256f62..ab8c088 100644 (file)
@@ -8,6 +8,6 @@
 #include <mruby.h>
 #include "mrdb.h"
 
-mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
+mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*, int);
 
 #endif /* APIPRINT_H_ */
index 8e59017..bc9937e 100644 (file)
@@ -242,7 +242,7 @@ info_break_select(mrb_state *mrb, mrdb_state *mrdb)
 }
 
 mrb_debug_bptype
-parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
+parse_breakcommand(mrb_state *mrb, mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
 {
   mrb_debug_context *dbg = mrdb->dbg;
   char *args;
@@ -274,7 +274,7 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
       STRTOUL(l, body);
       if (l <= 65535) {
         *line = l;
-        *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args;
+        *file = (body == args)? mrb_debug_get_filename(mrb, dbg->irep, dbg->pc - dbg->irep->iseq): args;
       }
       else {
         puts(BREAK_ERR_MSG_RANGEOVER);
@@ -332,7 +332,7 @@ dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
   char *method = NULL;
   int32_t ret;
 
-  type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
+  type = parse_breakcommand(mrb, mrdb, &file, &line, &cname, &method);
   switch (type) {
     case MRB_DEBUG_BPTYPE_LINE:
       ret = mrb_debug_set_break_line(mrb, dbg, file, line);
index 0a86456..db28e42 100644 (file)
@@ -82,6 +82,12 @@ static help_msg help_msg_list[] = {
     "Arguments are breakpoint numbers with spaces in between.\n"
   },
   {
+    "i[nfo]", "l[ocals]", "Print name of local variables",
+    "Usage: info locals\n"
+    "\n"
+    "Print name of local variables.\n"
+  },
+  {
     "l[ist]", NULL, "List specified line",
     "Usage: list\n"
     "       list first[,last]\n"
@@ -495,7 +501,7 @@ dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
 
   if (mrdb->dbg->xm == DBG_QUIT) {
     struct RClass *exc;
-    exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception"));
+    exc = mrb_define_class(mrb, "DebuggerExit", mrb->eException_class);
     mrb_raise(mrb, exc, "Exit mrdb.");
   }
   return DBGST_PROMPT;
index cca6367..25f0715 100644 (file)
@@ -36,7 +36,7 @@ dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
     expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
   }
 
-  result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL);
+  result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL, 0);
 
   /* $print_no = result */
   s = mrb_str_cat_lit(mrb, result, "\0");
@@ -56,3 +56,26 @@ dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
 {
   return dbgcmd_print(mrb, mrdb);
 }
+
+dbgcmd_state
+dbgcmd_info_local(mrb_state *mrb, mrdb_state *mrdb)
+{
+  mrb_value result;
+  mrb_value s;
+  int ai;
+
+  ai = mrb_gc_arena_save(mrb);
+
+  result = mrb_debug_eval(mrb, mrdb->dbg, "local_variables", 0, NULL, 1);
+
+  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;
+}
index cb4c738..233c86c 100644 (file)
@@ -19,7 +19,7 @@ dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
     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"));
+      exc = mrb_define_class(mrb, "DebuggerRestart", mrb->eException_class);
       mrb_raise(mrb, exc, "Restart mrdb.");
     }
   }
index 0588dfc..0034061 100644 (file)
@@ -52,6 +52,7 @@ static const debug_command debug_command_list[] = {
   {"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] */
+  {"info",      "locals",       1, 1, 0, DBGCMD_INFO_LOCAL,     dbgcmd_info_local},      /* i[nfo] l[ocals] */
   {"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] */
@@ -510,6 +511,7 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
   mrb_sym sym;
   int32_t bpno;
   mrb_bool isCfunc;
+  struct mrb_insn_data insn;
 
   mrb_debug_context *dbg = mrb_debug_context_get(mrb);
 
@@ -517,11 +519,12 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
   bpno = dbg->method_bpno;
   dbg->method_bpno = 0;
 
-  switch(GET_OPCODE(*pc)) {
+  insn = mrb_decode_insn(pc);
+  switch(insn.insn) {
     case OP_SEND:
     case OP_SENDB:
-      c = mrb_class(mrb, regs[GETARG_A(*pc)]);
-      sym = irep->syms[GETARG_B(*pc)];
+      c = mrb_class(mrb, regs[insn.a]);
+      sym = irep->syms[insn.b];
       break;
     case OP_SUPER:
       c = mrb->c->ci->target_class->super;
@@ -566,8 +569,8 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
     dbg->xphase = DBG_PHASE_RUNNING;
   }
 
-  file = mrb_debug_get_filename(irep, pc - irep->iseq);
-  line = mrb_debug_get_line(irep, pc - irep->iseq);
+  file = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
+  line = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
 
   switch (dbg->xm) {
   case DBG_STEP:
index 5ac12c1..7b14a89 100644 (file)
@@ -23,6 +23,7 @@ typedef enum debug_command_id {
   DBGCMD_STEP,
   DBGCMD_BREAK,
   DBGCMD_INFO_BREAK,
+  DBGCMD_INFO_LOCAL,
   DBGCMD_WATCH,
   DBGCMD_INFO_WATCH,
   DBGCMD_ENABLE,
@@ -151,6 +152,7 @@ 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_info_local(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*);
index ed53321..0058896 100644 (file)
@@ -10,3 +10,25 @@ 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
+
+assert('mirb -d option') do
+  o, _ = Open3.capture2('bin/mirb', :stdin_data => "$DEBUG\n")
+  assert_true o.include?('=> false')
+  o, _ = Open3.capture2('bin/mirb -d', :stdin_data => "$DEBUG\n")
+  assert_true o.include?('=> true')
+end
+
+assert('mirb -r option') do
+  lib = Tempfile.new('lib.rb')
+  lib.write <<EOS
+class Hoge
+  def hoge
+    :hoge
+  end
+end
+EOS
+  lib.flush
+
+  o, _ = Open3.capture2("bin/mirb -r #{lib.path}", :stdin_data => "Hoge.new.hoge\n")
+  assert_true o.include?('=> :hoge')
+end
index a58a72e..17b2ca1 100644 (file)
@@ -6,6 +6,15 @@
 ** immediately. It's a REPL...
 */
 
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/proc.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/throw.h>
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #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";
@@ -109,7 +112,7 @@ p(mrb_state *mrb, mrb_value obj, int prompt)
   if (!mrb_string_p(val)) {
     val = mrb_obj_as_string(mrb, obj);
   }
-  msg = mrb_locale_from_utf8(RSTRING_PTR(val), RSTRING_LEN(val));
+  msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val));
   fwrite(msg, strlen(msg), 1, stdout);
   mrb_locale_free(msg);
   putc('\n', stdout);
@@ -124,10 +127,6 @@ is_code_block_open(struct mrb_parser_state *parser)
 
   /* 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;
@@ -219,8 +218,11 @@ is_code_block_open(struct mrb_parser_state *parser)
 struct _args {
   FILE *rfp;
   mrb_bool verbose      : 1;
+  mrb_bool debug        : 1;
   int argc;
   char** argv;
+  int libc;
+  char **libv;
 };
 
 static void
@@ -228,6 +230,8 @@ usage(const char *name)
 {
   static const char *const usage_msg[] = {
   "switches:",
+  "-d           set $DEBUG to true (same as `mruby -d`)",
+  "-r library   same as `mruby -r`",
   "-v           print version number, then run in verbose mode",
   "--verbose    run in verbose mode",
   "--version    print the version",
@@ -241,9 +245,19 @@ usage(const char *name)
     printf("  %s\n", *p++);
 }
 
+static char *
+dup_arg_item(mrb_state *mrb, const char *item)
+{
+  size_t buflen = strlen(item) + 1;
+  char *buf = (char*)mrb_malloc(mrb, buflen);
+  memcpy(buf, item, buflen);
+  return buf;
+}
+
 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;
@@ -254,6 +268,26 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
 
     item = argv[0] + 1;
     switch (*item++) {
+    case 'd':
+      args->debug = TRUE;
+      break;
+    case 'r':
+      if (!item[0]) {
+        if (argc <= 1) {
+          printf("%s: No library specified for -r\n", *origargv);
+          return EXIT_FAILURE;
+        }
+        argc--; argv++;
+        item = argv[0];
+      }
+      if (args->libc == 0) {
+        args->libv = (char**)mrb_malloc(mrb, sizeof(char*));
+      }
+      else {
+        args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1));
+      }
+      args->libv[args->libc++] = dup_arg_item(mrb, item);
+      break;
     case 'v':
       if (!args->verbose) mrb_show_version(mrb);
       args->verbose = TRUE;
@@ -299,6 +333,12 @@ cleanup(mrb_state *mrb, struct _args *args)
   if (args->rfp)
     fclose(args->rfp);
   mrb_free(mrb, args->argv);
+  if (args->libc) {
+    while (args->libc--) {
+      mrb_free(mrb, args->libv[args->libc]);
+    }
+    mrb_free(mrb, args->libv);
+  }
   mrb_close(mrb);
 }
 
@@ -333,7 +373,7 @@ check_keyword(const char *buf, const char *word)
   size_t len = strlen(word);
 
   /* skip preceding spaces */
-  while (*p && isspace((unsigned char)*p)) {
+  while (*p && ISSPACE(*p)) {
     p++;
   }
   /* check keyword */
@@ -343,7 +383,7 @@ check_keyword(const char *buf, const char *word)
   p += len;
   /* skip trailing spaces */
   while (*p) {
-    if (!isspace((unsigned char)*p)) return 0;
+    if (!ISSPACE(*p)) return 0;
     p++;
   }
   return 1;
@@ -413,6 +453,7 @@ main(int argc, char **argv)
     }
   }
   mrb_define_global_const(mrb, "ARGV", ARGV);
+  mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug));
 
 #ifdef ENABLE_READLINE
   history_path = get_history_path(mrb);
@@ -429,6 +470,19 @@ main(int argc, char **argv)
   print_hint();
 
   cxt = mrbc_context_new(mrb);
+
+  /* Load libraries */
+  for (i = 0; i < args.libc; i++) {
+    FILE *lfp = fopen(args.libv[i], "r");
+    if (lfp == NULL) {
+      printf("Cannot open library file. (%s)\n", args.libv[i]);
+      cleanup(mrb, &args);
+      return EXIT_FAILURE;
+    }
+    mrb_load_file_cxt(mrb, lfp, cxt);
+    fclose(lfp);
+  }
+
   cxt->capture_errors = TRUE;
   cxt->lineno = 1;
   mrbc_filename(mrb, cxt, "(mirb)");
@@ -438,7 +492,10 @@ main(int argc, char **argv)
 
   while (TRUE) {
     char *utf8;
+    struct mrb_jmpbuf c_jmp;
 
+    MRB_TRY(&c_jmp);
+    mrb->jmp = &c_jmp;
     if (args.rfp) {
       if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
         goto done;
@@ -502,8 +559,7 @@ main(int argc, char **argv)
     MIRB_LINE_FREE(line);
 #endif
 
-done:
-
+  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);
@@ -542,13 +598,13 @@ done:
         /* warning */
         char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1);
         printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg);
-        mrb_utf8_free(msg);
+        mrb_locale_free(msg);
       }
       if (0 < parser->nerr) {
         /* syntax error */
         char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1);
         printf("line %d: %s\n", parser->error_buffer[0].lineno, msg);
-        mrb_utf8_free(msg);
+        mrb_locale_free(msg);
       }
       else {
         /* generate bytecode */
@@ -595,6 +651,11 @@ done:
     }
     mrb_parser_free(parser);
     cxt->lineno++;
+    MRB_CATCH(&c_jmp) {
+      p(mrb, mrb_obj_value(mrb->exc), 0);
+      mrb->exc = 0;
+    }
+    MRB_END_EXC(&c_jmp);
   }
 
 #ifdef ENABLE_READLINE
@@ -604,6 +665,12 @@ done:
 
   if (args.rfp) fclose(args.rfp);
   mrb_free(mrb, args.argv);
+  if (args.libv) {
+    for (i = 0; i < args.libc; ++i) {
+      mrb_free(mrb, args.libv[i]);
+    }
+    mrb_free(mrb, args.libv);
+  }
   mrbc_context_free(mrb, cxt);
   mrb_close(mrb);
 
index e710b5a..48b67ae 100644 (file)
@@ -8,7 +8,7 @@ MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec|
   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|
+  file exec => mrbc_objs + [build.libmruby_core_static] do |t|
     build.linker.run t.name, t.prerequisites
   end
 
index 580c2e2..2fd9da7 100644 (file)
@@ -18,6 +18,7 @@ struct mrbc_args {
   const char *initname;
   mrb_bool check_syntax : 1;
   mrb_bool verbose      : 1;
+  mrb_bool remove_lv    : 1;
   unsigned int flags    : 4;
 };
 
@@ -33,6 +34,7 @@ usage(const char *name)
   "-B<symbol>   binary <symbol> output in C language format",
   "-e           generate little endian iseq data",
   "-E           generate big endian iseq data",
+  "--remove-lv  remove local variables",
   "--verbose    run at verbose mode",
   "--version    print the version",
   "--copyright  print the copyright",
@@ -142,6 +144,10 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
           mrb_show_copyright(mrb);
           exit(EXIT_SUCCESS);
         }
+        else if (strcmp(argv[i] + 2, "remove-lv") == 0) {
+          args->remove_lv = TRUE;
+          break;
+        }
         return -1;
       default:
         return i;
@@ -232,6 +238,9 @@ dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, st
   int n = MRB_DUMP_OK;
   mrb_irep *irep = proc->body.irep;
 
+  if (args->remove_lv) {
+    mrb_irep_remove_lv(mrb, irep);
+  }
   if (args->initname) {
     n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
     if (n == MRB_DUMP_INVALID_ARGUMENT) {
diff --git a/third-party/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/third-party/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake
deleted file mode 100644 (file)
index 66d6ef8..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-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 b6b0901..e8d1510 100644 (file)
@@ -12,7 +12,7 @@ assert('regression for #1572') do
   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"'
+  assert_equal '"ok"', o
 end
 
 assert '$0 value' do
@@ -31,6 +31,13 @@ assert '$0 value' do
   assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp
 end
 
+assert('float literal') do
+  script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
+  File.write script.path, 'p [3.21, 2e308.infinite?, -2e308.infinite?]'
+  system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}"
+  assert_equal "[3.21, 1, -1]", `#{cmd('mruby')} -b #{bin.path}`.chomp!
+end
+
 assert '__END__', '8.6' do
   script = Tempfile.new('test.rb')
 
@@ -58,3 +65,33 @@ RUBY
   assert_equal "NilClass", `#{cmd('mruby')} #{script.path}`
   assert_equal 0, $?.exitstatus
 end
+
+assert('mruby -d option') do
+  o = `#{cmd('mruby')} -e #{shellquote('p $DEBUG')}`
+  assert_equal "false\n", o
+  o = `#{cmd('mruby')} -d -e #{shellquote('p $DEBUG')}`
+  assert_equal "true\n", o
+end
+
+assert('mruby -r option') do
+  lib = Tempfile.new('lib.rb')
+  lib.write <<EOS
+class Hoge
+  def hoge
+    :hoge
+  end
+end
+EOS
+  lib.flush
+
+  script = Tempfile.new('test.rb')
+  script.write <<EOS
+print Hoge.new.hoge
+EOS
+  script.flush
+  assert_equal 'hoge', `#{cmd('mruby')} -r #{lib.path} #{script.path}`
+  assert_equal 0, $?.exitstatus
+
+  assert_equal 'hogeClass', `#{cmd('mruby')} -r #{lib.path} -r #{script.path} -e #{shellquote('print Hoge.class')}`
+  assert_equal 0, $?.exitstatus
+end
index fbec138..280621e 100644 (file)
@@ -5,6 +5,7 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
   spec.bins = %w(mruby)
   spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
   spec.add_dependency('mruby-error', :core => 'mruby-error')
+  spec.add_test_dependency('mruby-print', :core => 'mruby-print')
 
   if build.cxx_exception_enabled?
     build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx")
index 61d4cde..498bede 100644 (file)
@@ -27,8 +27,11 @@ struct _args {
   mrb_bool mrbfile      : 1;
   mrb_bool check_syntax : 1;
   mrb_bool verbose      : 1;
+  mrb_bool debug        : 1;
   int argc;
   char** argv;
+  int libc;
+  char **libv;
 };
 
 static void
@@ -38,7 +41,9 @@ usage(const char *name)
   "switches:",
   "-b           load and execute RiteBinary (mrb) file",
   "-c           check syntax only",
+  "-d           set debugging flags (set $DEBUG to true)",
   "-e 'command' one line of script",
+  "-r library   load the library before executing your script",
   "-v           print version number, then run in verbose mode",
   "--verbose    run in verbose mode",
   "--version    print the version",
@@ -52,6 +57,15 @@ usage(const char *name)
     printf("  %s\n", *p++);
 }
 
+static char *
+dup_arg_item(mrb_state *mrb, const char *item)
+{
+  size_t buflen = strlen(item) + 1;
+  char *buf = (char*)mrb_malloc(mrb, buflen);
+  memcpy(buf, item, buflen);
+  return buf;
+}
+
 static int
 parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
 {
@@ -78,6 +92,9 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
     case 'c':
       args->check_syntax = TRUE;
       break;
+    case 'd':
+      args->debug = TRUE;
+      break;
     case 'e':
       if (item[0]) {
         goto append_cmdline;
@@ -87,13 +104,7 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
         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;
+          args->cmdline = dup_arg_item(mrb, item);
         }
         else {
           size_t cmdlinelen;
@@ -112,6 +123,23 @@ append_cmdline:
         return EXIT_SUCCESS;
       }
       break;
+    case 'r':
+      if (!item[0]) {
+        if (argc <= 1) {
+          printf("%s: No library specified for -r\n", *origargv);
+          return EXIT_FAILURE;
+        }
+        argc--; argv++;
+        item = argv[0];
+      }
+      if (args->libc == 0) {
+        args->libv = (char**)mrb_malloc(mrb, sizeof(char*));
+      }
+      else {
+        args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1));
+      }
+      args->libv[args->libc++] = dup_arg_item(mrb, item);
+      break;
     case 'v':
       if (!args->verbose) mrb_show_version(mrb);
       args->verbose = TRUE;
@@ -162,6 +190,12 @@ cleanup(mrb_state *mrb, struct _args *args)
   if (!args->fname)
     mrb_free(mrb, args->cmdline);
   mrb_free(mrb, args->argv);
+  if (args->libc) {
+    while (args->libc--) {
+      mrb_free(mrb, args->libv[args->libc]);
+    }
+    mrb_free(mrb, args->libv);
+  }
   mrb_close(mrb);
 }
 
@@ -199,6 +233,7 @@ main(int argc, char **argv)
       }
     }
     mrb_define_global_const(mrb, "ARGV", ARGV);
+    mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug));
 
     c = mrbc_context_new(mrb);
     if (args.verbose)
@@ -219,6 +254,24 @@ main(int argc, char **argv)
       mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
     }
 
+    /* Load libraries */
+    for (i = 0; i < args.libc; i++) {
+      FILE *lfp = fopen(args.libv[i], args.mrbfile ? "rb" : "r");
+      if (lfp == NULL) {
+        printf("Cannot open library file: %s\n", args.libv[i]);
+        mrbc_context_free(mrb, c);
+        cleanup(mrb, &args);
+        return EXIT_FAILURE;
+      }
+      if (args.mrbfile) {
+        v = mrb_load_irep_file_cxt(mrb, lfp, c);
+      }
+      else {
+        v = mrb_load_file_cxt(mrb, lfp, c);
+      }
+      fclose(lfp);
+    }
+
     /* Load program */
     if (args.mrbfile) {
       v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
index bb664a2..2db3c10 100644 (file)
@@ -67,7 +67,7 @@ EOS
 
   `#{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
+#
+#  assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp
+#  assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp
 end
index deb66d5..fb78b0c 100644 (file)
@@ -12,22 +12,6 @@ struct strip_args {
   mrb_bool lvar;
 };
 
-
-static void
-irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
-{
-  int 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)
 {
@@ -99,7 +83,7 @@ strip(mrb_state *mrb, struct strip_args *args)
 
     /* clear lv if --lvar is enabled */
     if (args->lvar) {
-      irep_remove_lv(mrb, irep);
+      mrb_irep_remove_lv(mrb, irep);
     }
 
     wfile = fopen(filename, "wb");
diff --git a/third-party/mruby/mrbgems/mruby-class-ext/mrblib/module.rb b/third-party/mruby/mrbgems/mruby-class-ext/mrblib/module.rb
new file mode 100644 (file)
index 0000000..3015851
--- /dev/null
@@ -0,0 +1,89 @@
+class Module
+
+  ##
+  # call-seq:
+  #   mod < other   ->  true, false, or nil
+  #
+  # Returns true if `mod` is a subclass of `other`. Returns
+  # <code>nil</code> if there's no relationship between the two.
+  # (Think of the relationship in terms of the class definition:
+  # "class A < B" implies "A < B".)
+  #
+  def <(other)
+    if self.equal?(other)
+      false
+    else
+      self <= other
+    end
+  end
+
+  ##
+  # call-seq:
+  #   mod <= other   ->  true, false, or nil
+  #
+  # Returns true if `mod` is a subclass of `other` or
+  # is the same as `other`. Returns
+  # <code>nil</code> if there's no relationship between the two.
+  # (Think of the relationship in terms of the class definition:
+  # "class A < B" implies "A < B".)
+  def <=(other)
+    raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
+    if self.ancestors.include?(other)
+      return true
+    elsif other.ancestors.include?(self)
+      return false
+    end
+  end
+
+  ##
+  # call-seq:
+  #  mod > other   ->  true, false, or nil
+  #
+  # Returns true if `mod` is an ancestor of `other`. Returns
+  # <code>nil</code> if there's no relationship between the two.
+  # (Think of the relationship in terms of the class definition:
+  # "class A < B" implies "B > A".)
+  #
+  def >(other)
+    if self.equal?(other)
+      false
+    else
+      self >= other
+    end
+  end
+
+  ##
+  # call-seq:
+  #   mod >= other   ->  true, false, or nil
+  #
+  # Returns true if `mod` is an ancestor of `other`, or the
+  # two modules are the same. Returns
+  # <code>nil</code> if there's no relationship between the two.
+  # (Think of the relationship in terms of the class definition:
+  # "class A < B" implies "B > A".)
+  #
+  def >=(other)
+    raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
+    return other < self
+  end
+
+  ##
+  # call-seq:
+  #    module <=> other_module   -> -1, 0, +1, or nil
+  #
+  # Comparison---Returns -1, 0, +1 or nil depending on whether `module`
+  # includes `other_module`, they are the same, or if `module` is included by
+  # `other_module`.
+  #
+  # Returns `nil` if `module` has no relationship with `other_module`, if
+  # `other_module` is not a module, or if the two values are incomparable.
+  #
+  def <=>(other)
+    return 0 if self.equal?(other)
+    return nil unless other.is_a?(Module)
+    cmp = self < other
+    return -1 if cmp
+    return 1 unless cmp.nil?
+    return nil
+  end
+end
index ed6713a..52e04ab 100644 (file)
@@ -1,3 +1,57 @@
+assert 'Module#<' do
+  a = Class.new
+  b = Class.new(a)
+  c = Class.new(a)
+  d = Module.new
+  e = Class.new { include d }
+  f = Module.new { include d }
+
+  # compare class to class
+  assert_true b < a
+  assert_false b < b
+  assert_false a < b
+  assert_nil c < b
+
+  # compare class to module
+  assert_true e < d
+  assert_false d < e
+  assert_nil a < d
+
+  # compare module to module
+  assert_true f < d
+  assert_false f < f
+  assert_false d < f
+
+  assert_raise(TypeError) { a < Object.new }
+end
+
+assert 'Module#<=' do
+  a = Class.new
+  b = Class.new(a)
+  c = Class.new(a)
+  d = Module.new
+  e = Class.new { include d }
+  f = Module.new { include d }
+
+  # compare class to class
+  assert_true b <= a
+  assert_true b <= b
+  assert_false a <= b
+  assert_nil c <= b
+
+  # compare class to module
+  assert_true e <= d
+  assert_false d <= e
+  assert_nil a <= d
+
+  # compare module to module
+  assert_true f <= d
+  assert_true f <= f
+  assert_false d <= f
+
+  assert_raise(TypeError) { a <= Object.new }
+end
+
 assert 'Module#name' do
   module Outer
     class Inner; end
@@ -26,7 +80,7 @@ end
 assert 'Module#singleton_class?' do
   mod = Module.new
   cls = Class.new
-  scl = cls.singleton_class
+  scl = (class <<cls; self; end)
 
   assert_false mod.singleton_class?
   assert_false cls.singleton_class?
index eb82110..927cc3a 100644 (file)
@@ -8,6 +8,7 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 #include <mruby.h>
 #include <mruby/compile.h>
 #include <mruby/proc.h>
@@ -23,6 +24,8 @@
 #define MRB_CODEGEN_LEVEL_MAX 1024
 #endif
 
+#define MAXARG_S (1<<16)
+
 typedef mrb_ast_node node;
 typedef struct mrb_parser_state parser_state;
 
@@ -36,7 +39,7 @@ enum looptype {
 
 struct loopinfo {
   enum looptype type;
-  int pc1, pc2, pc3, acc;
+  int pc0, pc1, pc2, pc3, acc;
   int ensure_level;
   struct loopinfo *prev;
 };
@@ -50,23 +53,24 @@ typedef struct scope {
 
   node *lv;
 
-  int sp;
-  int pc;
-  int lastlabel;
+  uint16_t sp;
+  uint16_t pc;
+  uint16_t lastpc;
+  uint16_t lastlabel;
   int ainfo:15;
   mrb_bool mscope:1;
 
   struct loopinfo *loop;
   int ensure_level;
-  char const *filename;
+  mrb_sym filename_sym;
   uint16_t lineno;
 
   mrb_code *iseq;
   uint16_t *lines;
-  int icapa;
+  uint32_t icapa;
 
   mrb_irep *irep;
-  int pcapa, scapa, rcapa;
+  uint32_t pcapa, scapa, rcapa;
 
   uint16_t nlocals;
   uint16_t nregs;
@@ -102,8 +106,9 @@ codegen_error(codegen_scope *s, const char *message)
     s = tmp;
   }
 #ifndef MRB_DISABLE_STDIO
-  if (s->filename && s->lineno) {
-    fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
+  if (s->filename_sym && s->lineno) {
+    const char *filename = mrb_sym2name_len(s->mrb, s->filename_sym, NULL);
+    fprintf(stderr, "codegen error:%s:%d: %s\n", filename, s->lineno, message);
   }
   else {
     fprintf(stderr, "codegen error: %s\n", message);
@@ -122,15 +127,6 @@ codegen_palloc(codegen_scope *s, size_t len)
 }
 
 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);
@@ -142,38 +138,142 @@ codegen_realloc(codegen_scope *s, void *p, size_t len)
 static int
 new_label(codegen_scope *s)
 {
-  s->lastlabel = s->pc;
-  return s->pc;
+  return s->lastlabel = s->pc;
 }
 
-static inline int
-genop(codegen_scope *s, mrb_code i)
+static void
+emit_B(codegen_scope *s, uint32_t pc, uint8_t i)
 {
-  if (s->pc >= s->icapa) {
+  if (pc >= MAXARG_S || s->icapa >= MAXARG_S) {
+    codegen_error(s, "too big code block");
+  }
+  if (pc >= s->icapa) {
     s->icapa *= 2;
-    if (s->pc >= MAXARG_sBx) {
-      codegen_error(s, "too big code block");
-    }
-    if (s->icapa > MAXARG_sBx) {
-      s->icapa = MAXARG_sBx;
+    if (s->icapa > MAXARG_S) {
+      s->icapa = MAXARG_S;
     }
     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->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->icapa);
     }
   }
-  s->iseq[s->pc] = i;
   if (s->lines) {
-    s->lines[s->pc] = s->lineno;
+    if (s->lineno > 0 || pc == 0)
+      s->lines[pc] = s->lineno;
+    else
+      s->lines[pc] = s->lines[pc-1];
+  }
+  s->iseq[pc] = i;
+}
+
+static void
+emit_S(codegen_scope *s, int pc, uint16_t i)
+{
+  uint8_t hi = i>>8;
+  uint8_t lo = i&0xff;
+
+  emit_B(s, pc,   hi);
+  emit_B(s, pc+1, lo);
+}
+
+static void
+gen_B(codegen_scope *s, uint8_t i)
+{
+  emit_B(s, s->pc, i);
+  s->pc++;
+}
+
+static void
+gen_S(codegen_scope *s, uint16_t i)
+{
+  emit_S(s, s->pc, i);
+  s->pc += 2;
+}
+
+static void
+genop_0(codegen_scope *s, mrb_code i)
+{
+  s->lastpc = s->pc;
+  gen_B(s, i);
+}
+
+static void
+genop_1(codegen_scope *s, mrb_code i, uint16_t a)
+{
+  s->lastpc = s->pc;
+  if (a > 0xff) {
+    gen_B(s, OP_EXT1);
+    gen_B(s, i);
+    gen_S(s, a);
+  }
+  else {
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
   }
-  return s->pc++;
+}
+
+static void
+genop_2(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+  s->lastpc = s->pc;
+  if (a > 0xff && b > 0xff) {
+    gen_B(s, OP_EXT3);
+    gen_B(s, i);
+    gen_S(s, a);
+    gen_S(s, b);
+  }
+  else if (b > 0xff) {
+    gen_B(s, OP_EXT2);
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
+    gen_S(s, b);
+  }
+  else if (a > 0xff) {
+    gen_B(s, OP_EXT1);
+    gen_B(s, i);
+    gen_S(s, a);
+    gen_B(s, (uint8_t)b);
+  }
+  else {
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
+    gen_B(s, (uint8_t)b);
+  }
+}
+
+static void
+genop_3(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b, uint8_t c)
+{
+  genop_2(s, i, a, b);
+  gen_B(s, c);
+}
+
+static void
+genop_2S(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+  genop_1(s, i, a);
+  gen_S(s, b);
+}
+
+static void
+genop_W(codegen_scope *s, mrb_code i, uint32_t a)
+{
+  uint8_t a1 = (a>>16) & 0xff;
+  uint8_t a2 = (a>>8) & 0xff;
+  uint8_t a3 = a & 0xff;
+
+  s->lastpc = s->pc;
+  gen_B(s, i);
+  gen_B(s, a1);
+  gen_B(s, a2);
+  gen_B(s, a3);
 }
 
 #define NOVAL  0
 #define VAL    1
 
-static mrb_bool
+//static
+mrb_bool
 no_optimize(codegen_scope *s)
 {
   if (s && s->parser && s->parser->no_optimize)
@@ -181,298 +281,270 @@ no_optimize(codegen_scope *s)
   return FALSE;
 }
 
-static int
-genop_peep(codegen_scope *s, mrb_code i, int val)
+static
+mrb_bool
+on_eval(codegen_scope *s)
 {
-  /* 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);
+  if (s && s->parser && s->parser->on_eval)
+    return TRUE;
+  return FALSE;
+}
 
-    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_GETUPVAR:
-      if (c0 == OP_SETUPVAR) {
-        if (GETARG_B(i) == GETARG_B(i0) && GETARG_C(i) == GETARG_C(i0)) {
-          if (GETARG_A(i) == GETARG_A(i0)) {
-            /* just skip OP_SETUPVAR */
-            return 0;
-          }
-          else {
-            return genop(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_A(i0)));
-          }
-        }
-      }
-      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;
-      }
-      break;
-    case OP_ARYCAT:
-    case OP_ARYPUSH:
-      if (c0 == OP_MOVE && GETARG_A(i0) >= s->nlocals) {
-        s->iseq[s->pc-1] = MKOP_AB(c1, GETARG_A(i), GETARG_B(i0));
-        return 0;
-      }
-      break;
-    case OP_STRCAT:
-      if (c0 == OP_STRING) {
-        mrb_value v = s->irep->pool[GETARG_Bx(i0)];
+struct mrb_insn_data
+mrb_decode_insn(mrb_code *pc)
+{
+  struct mrb_insn_data data = { 0 };
+  mrb_code insn = READ_B();
+  uint16_t a = 0;
+  uint16_t b = 0;
+  uint8_t  c = 0;
+
+  switch (insn) {
+#define FETCH_Z() /* empty */
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+  }
+  switch (insn) {
+  case OP_EXT1:
+    insn = READ_B();
+    switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+    }
+    break;
+  case OP_EXT2:
+    insn = READ_B();
+    switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+    }
+    break;
+  case OP_EXT3:
+    insn = READ_B();
+    switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+    }
+    break;
+  default:
+    break;
+  }
+  data.insn = insn;
+  data.a = a;
+  data.b = b;
+  data.c = c;
+  return data;
+}
 
-        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;
-        }
-      }
+static struct mrb_insn_data
+mrb_last_insn(codegen_scope *s)
+{
+  if (s->pc == s->lastpc) {
+    struct mrb_insn_data data;
+
+    data.insn = OP_NOP;
+    return data;
+  }
+  return mrb_decode_insn(&s->iseq[s->lastpc]);
+}
+
+static mrb_bool
+no_peephole(codegen_scope *s)
+{
+  return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc;
+}
+
+static uint16_t
+genjmp(codegen_scope *s, mrb_code i, uint16_t pc)
+{
+  uint16_t pos;
+
+  s->lastpc = s->pc;
+  gen_B(s, i);
+  pos = s->pc;
+  gen_S(s, pc);
+  return pos;
+}
+
+static uint16_t
+genjmp2(codegen_scope *s, mrb_code i, uint16_t a, int pc, int val)
+{
+  uint16_t pos;
+
+  if (!no_peephole(s) && !val) {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    if (data.insn == OP_MOVE && data.a == a) {
+      s->pc = s->lastpc;
+      a = data.b;
+    }
+  }
+
+  s->lastpc = s->pc;
+  if (a > 0xff) {
+    gen_B(s, OP_EXT1);
+    gen_B(s, i);
+    gen_S(s, a);
+    pos = s->pc;
+    gen_S(s, pc);
+  }
+  else {
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
+    pos = s->pc;
+    gen_S(s, pc);
+  }
+  return pos;
+}
+
+static void
+gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
+{
+  if (no_peephole(s)) {
+  normal:
+    genop_2(s, OP_MOVE, dst, src);
+    if (on_eval(s)) {
+      genop_0(s, OP_NOP);
+    }
+    return;
+  }
+  else {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    switch (data.insn) {
+    case OP_MOVE:
+      if (dst == src) return;             /* remove useless MOVE */
+      if (data.b == dst && data.a == src) /* skip swapping MOVE */
+        return;
+      goto normal;
+    case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF:
+    case OP_LOADI__1:
+    case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+    case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+      if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+      s->pc = s->lastpc;
+      genop_1(s, data.insn, dst);
       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;
-      }
+    case OP_LOADI: case OP_LOADINEG: case OP_LOADL: case OP_LOADSYM:
+    case OP_GETGV: case OP_GETSV: case OP_GETIV: case OP_GETCV:
+    case OP_GETCONST: case OP_STRING:
+    case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH:
+      if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+      s->pc = s->lastpc;
+      genop_2(s, data.insn, dst, data.b);
       break;
     default:
-      break;
+      goto normal;
     }
   }
-  return genop(s, i);
 }
 
 static void
-scope_error(codegen_scope *s)
+gen_return(codegen_scope *s, uint8_t op, uint16_t src)
 {
-  exit(EXIT_FAILURE);
+  if (no_peephole(s)) {
+    genop_1(s, op, src);
+  }
+  else {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    if (data.insn == OP_MOVE && src == data.a) {
+      s->pc = s->lastpc;
+      genop_1(s, op, data.b);
+    }
+    else if (data.insn != OP_RETURN) {
+      genop_1(s, op, src);
+    }
+  }
 }
 
 static void
-distcheck(codegen_scope *s, int diff)
+gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
 {
-  if (diff > MAXARG_sBx || diff < -MAXARG_sBx) {
-    codegen_error(s, "too distant jump address");
+  if (no_peephole(s)) {
+  normal:
+    genop_1(s, op, dst);
+    return;
+  }
+  else {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    switch (data.insn) {
+    case OP_LOADI__1:
+      if (op == OP_ADD) op = OP_SUB;
+      else op = OP_ADD;
+      data.b = 1;
+      goto replace;
+    case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+    case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+      data.b = data.insn - OP_LOADI_0;
+      /* fall through */
+    case OP_LOADI:
+    replace:
+      if (data.b >= 128) goto normal;
+      s->pc = s->lastpc;
+      if (op == OP_ADD) {
+        genop_2(s, OP_ADDI, dst, (uint8_t)data.b);
+      }
+      else {
+        genop_2(s, OP_SUBI, dst, (uint8_t)data.b);
+      }
+      break;
+    default:
+      goto normal;
+    }
   }
 }
 
-static inline void
-dispatch(codegen_scope *s, int pc)
+static int
+dispatch(codegen_scope *s, uint16_t pos0)
 {
-  int diff = s->pc - pc;
-  mrb_code i = s->iseq[pc];
-  int c = GET_OPCODE(i);
+  uint16_t newpos;
 
   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;
-  }
-  distcheck(s, diff);
-  s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
+  newpos = PEEK_S(s->iseq+pos0);
+  emit_S(s, pos0, s->pc);
+  return newpos;
 }
 
 static void
-dispatch_linked(codegen_scope *s, int pc)
+dispatch_linked(codegen_scope *s, uint16_t pos)
 {
-  mrb_code i;
-  int pos;
-
-  if (!pc) return;
+  if (pos==0) return;
   for (;;) {
-    i = s->iseq[pc];
-    pos = GETARG_sBx(i);
-    dispatch(s, pc);
-    if (!pos) break;
-    pc = pos;
+    pos = dispatch(s, pos);
+    if (pos==0) break;
   }
 }
 
 #define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
 static void
-push_(codegen_scope *s)
+push_n_(codegen_scope *s, int n)
 {
-  if (s->sp > 511) {
+  if (s->sp+n >= 0xffff) {
     codegen_error(s, "too complex expression");
   }
-  s->sp++;
+  s->sp+=n;
   nregs_update;
 }
 
 static void
-push_n_(codegen_scope *s, int n)
+pop_n_(codegen_scope *s, int n)
 {
-  if (s->sp+n > 511) {
-    codegen_error(s, "too complex expression");
+  if ((int)s->sp-n < 0) {
+    codegen_error(s, "stack pointer underflow");
   }
-  s->sp+=n;
-  nregs_update;
+  s->sp-=n;
 }
 
-#define push() push_(s)
+#define push() push_n_(s,1)
 #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 pop() pop_n_(s,1)
+#define pop_n(n) pop_n_(s,n)
 #define cursp() (s->sp)
 
 static inline int
@@ -496,9 +568,12 @@ new_lit(codegen_scope *s, mrb_value val)
 #ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     for (i=0; i<s->irep->plen; i++) {
+      mrb_float f1, f2;
       pv = &s->irep->pool[i];
       if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
-      if (mrb_float(*pv) == mrb_float(val)) return i;
+      f1 = mrb_float(*pv);
+      f2 = mrb_float(val);
+      if (f1 == f2 && !signbit(f1) == !signbit(f2)) return i;
     }
     break;
 #endif
@@ -545,52 +620,23 @@ new_lit(codegen_scope *s, mrb_value val)
   return i;
 }
 
-/* method symbols should be fit in 9 bits */
-#define MAXMSYMLEN 512
 /* maximum symbol numbers */
-#define MAXSYMLEN 65536
+#define MAXSYMLEN 0x10000
 
 static int
-new_msym(codegen_scope *s, mrb_sym sym)
+new_sym(codegen_scope *s, mrb_sym sym)
 {
   int 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)
-{
-  int 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;
+  if (s->irep->slen >= s->scapa) {
+    s->scapa *= 2;
+    s->irep->syms = (mrb_sym*)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*s->scapa);
   }
   s->irep->syms[s->irep->slen] = sym;
   return s->irep->slen++;
@@ -608,8 +654,12 @@ node_len(node *tree)
   return n;
 }
 
+#define nint(x) ((int)(intptr_t)(x))
+#define nchar(x) ((char)(intptr_t)(x))
 #define nsym(x) ((mrb_sym)(intptr_t)(x))
+
 #define lv_name(lv) nsym((lv)->car)
+
 static int
 lv_idx(codegen_scope *s, mrb_sym id)
 {
@@ -631,7 +681,6 @@ for_body(codegen_scope *s, node *tree)
   int idx;
   struct loopinfo *lp;
   node *n2;
-  mrb_code c;
 
   /* generate receiver */
   codegen(s, tree->cdr->car, VAL);
@@ -645,7 +694,7 @@ for_body(codegen_scope *s, node *tree)
 
   /* generate loop variable */
   n2 = tree->car;
-  genop(s, MKOP_Ax(OP_ENTER, 0x40000));
+  genop_W(s, OP_ENTER, 0x40000);
   if (n2->car && !n2->car->cdr && !n2->cdr) {
     gen_assignment(s, n2->car->car, 1, NOVAL);
   }
@@ -659,25 +708,20 @@ for_body(codegen_scope *s, node *tree)
   /* 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);
-  }
+  gen_return(s, OP_RETURN, cursp());
   loop_pop(s, NOVAL);
   scope_finish(s);
   s = prev;
-  genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
+  genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
   push();pop(); /* space for a block */
   pop();
-  idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
-  genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
+  idx = new_sym(s, mrb_intern_lit(s->mrb, "each"));
+  genop_3(s, 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) {
@@ -688,78 +732,150 @@ lambda_body(codegen_scope *s, node *tree, int blk)
 
   if (blk) {
     struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
-    lp->pc1 = new_label(s);
+    lp->pc0 = new_label(s);
   }
   tree = tree->cdr;
-  if (tree->car) {
+  if (tree->car == NULL) {
+    genop_W(s, OP_ENTER, 0);
+  }
+  else {
     mrb_aspec a;
     int ma, oa, ra, pa, ka, kd, ba;
     int pos, i;
-    node *n, *opt;
+    node *opt;
+    node *margs, *pargs;
+    node *tail;
 
+    /* mandatory arguments */
     ma = node_len(tree->car->car);
-    n = tree->car->car;
-    while (n) {
-      n = n->cdr;
-    }
+    margs = tree->car->car;
+    tail = tree->car->cdr->cdr->cdr->cdr;
+
+    /* optional arguments */
     oa = node_len(tree->car->cdr->car);
+    /* rest argument? */
     ra = tree->car->cdr->cdr->car ? 1 : 0;
+    /* mandatory arugments after rest argument */
     pa = node_len(tree->car->cdr->cdr->cdr->car);
-    ka = kd = 0;
-    ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+    pargs = tree->car->cdr->cdr->cdr->car;
+    /* keyword arguments */
+    ka = tail? node_len(tail->cdr->car) : 0;
+    /* keyword dictionary? */
+    kd = tail && tail->cdr->cdr->car? 1 : 0;
+    /* block argument? */
+    ba = tail && tail->cdr->cdr->cdr->car ? 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));
+    a = MRB_ARGS_REQ(ma)
+      | MRB_ARGS_OPT(oa)
+      | (ra? MRB_ARGS_REST() : 0)
+      | MRB_ARGS_POST(pa)
+      | MRB_ARGS_KEY(ka, kd)
+      | (ba? MRB_ARGS_BLOCK() : 0);
+    s->ainfo = (((ma+oa) & 0x3f) << 7) /* (12bits = 5:1:5:1) */
+      | ((ra & 0x1) << 6)
+      | ((pa & 0x1f) << 1)
+      | (kd & 0x1);
+    genop_W(s, OP_ENTER, a);
+    /* generate jump table for optional arguments initializer */
     pos = new_label(s);
     for (i=0; i<oa; i++) {
       new_label(s);
-      genop(s, MKOP_sBx(OP_JMP, 0));
+      genjmp(s, OP_JMP, 0);
     }
     if (oa > 0) {
-      genop(s, MKOP_sBx(OP_JMP, 0));
+      genjmp(s, OP_JMP, 0);
     }
     opt = tree->car->cdr->car;
     i = 0;
     while (opt) {
       int idx;
 
-      dispatch(s, pos+i);
+      dispatch(s, pos+i*3+1);
       codegen(s, opt->car->cdr, VAL);
-      idx = lv_idx(s, nsym(opt->car->car));
       pop();
-      genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
+      idx = lv_idx(s, nsym(opt->car->car));
+      gen_move(s, idx, cursp(), 0);
       i++;
       opt = opt->cdr;
     }
     if (oa > 0) {
-      dispatch(s, pos+i);
+      dispatch(s, pos+i*3+1);
+    }
+
+    /* keyword arguments */
+    if (tail) {
+      node *kwds = tail->cdr->car;
+      int kwrest = 0;
+
+      if (tail->cdr->cdr->car) {
+        kwrest = 1;
+      }
+      mrb_assert(nint(tail->car) == NODE_ARGS_TAIL);
+      mrb_assert(node_len(tail) == 4);
+
+      while (kwds) {
+        int jmpif_key_p, jmp_def_set = -1;
+        node *kwd = kwds->car, *def_arg = kwd->cdr->cdr->car;
+        mrb_sym kwd_sym = nsym(kwd->cdr->car);
+
+        mrb_assert(nint(kwd->car) == NODE_KW_ARG);
+
+        if (def_arg) {
+          genop_2(s, OP_KEY_P, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+          jmpif_key_p = genjmp2(s, OP_JMPIF, lv_idx(s, kwd_sym), 0, 0);
+          codegen(s, def_arg, VAL);
+          pop();
+          gen_move(s, lv_idx(s, kwd_sym), cursp(), 0);
+          jmp_def_set = genjmp(s, OP_JMP, 0);
+          dispatch(s, jmpif_key_p);
+        }
+        genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+        if (jmp_def_set != -1) {
+          dispatch(s, jmp_def_set);
+        }
+        i++;
+
+        kwds = kwds->cdr;
+      }
+      if (tail->cdr->car && !kwrest) {
+        genop_0(s, OP_KEYEND);
+      }
+    }
+
+    /* argument destructuring */
+    if (margs) {
+      node *n = margs;
+
+      pos = 1;
+      while (n) {
+        if (nint(n->car->car) == NODE_MASGN) {
+          gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
+        }
+        pos++;
+        n = n->cdr;
+      }
+    }
+    if (pargs) {
+      node *n = margs;
+
+      pos = ma+oa+ra+1;
+      while (n) {
+        if (nint(n->car->car) == NODE_MASGN) {
+          gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
+        }
+        pos++;
+        n = n->cdr;
+      }
     }
   }
+
   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);
-      }
-    }
+    gen_return(s, OP_RETURN, cursp());
   }
   if (blk) {
     loop_pop(s, NOVAL);
@@ -773,24 +889,13 @@ 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_error(s, "unexpected scope");
   }
 
   codegen(scope, tree->cdr, VAL);
+  gen_return(scope, OP_RETURN, scope->sp-1);
   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);
-    }
+    genop_0(scope, OP_STOP);
   }
   scope_finish(scope);
   if (!s->irep) {
@@ -800,9 +905,6 @@ scope_body(codegen_scope *s, node *tree, int val)
   return s->irep->rlen - 1;
 }
 
-#define nint(x) ((int)(intptr_t)(x))
-#define nchar(x) ((char)(intptr_t)(x))
-
 static mrb_bool
 nosplat(node *t)
 {
@@ -854,15 +956,15 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
         }
         else {
           pop_n(n);
-          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+          genop_2(s, OP_ARRAY, cursp(), n);
           push();
           codegen(s, t->car, VAL);
           pop(); pop();
           if (is_splat) {
-            genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
+            genop_1(s, OP_ARYCAT, cursp());
           }
           else {
-            genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
+            genop_1(s, OP_ARYPUSH, cursp());
           }
         }
         t = t->cdr;
@@ -871,10 +973,10 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
           codegen(s, t->car, VAL);
           pop(); pop();
           if (nint(t->car->car) == NODE_SPLAT) {
-            genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
+            genop_1(s, OP_ARYCAT, cursp());
           }
           else {
-            genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
+            genop_1(s, OP_ARYPUSH, cursp());
           }
           t = t->cdr;
         }
@@ -899,22 +1001,15 @@ static void
 gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
 {
   mrb_sym sym = name ? name : nsym(tree->cdr->car);
-  int idx, skip = 0;
+  int 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_n(2); pop_n(2); /* space for one arg and 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));
+    gen_move(s, cursp(), recv, 1);
+    skip = genjmp2(s, OP_JMPNIL, cursp(), 0, val);
   }
-  idx = new_msym(s, sym);
   tree = tree->cdr->cdr->car;
   if (tree) {
     n = gen_values(s, tree->car, VAL, sp?1:0);
@@ -923,14 +1018,15 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
       push();
     }
   }
-  if (sp) {
+  if (sp) {                     /* last argument pushed (attr=) */
     if (sendv) {
+      gen_move(s, cursp(), sp, 0);
       pop();
-      genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
+      genop_1(s, OP_ARYPUSH, cursp());
       push();
     }
     else {
-      genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
+      gen_move(s, cursp(), sp, 0);
       push();
       n++;
     }
@@ -939,9 +1035,7 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
     noop = 1;
     codegen(s, tree->cdr, VAL);
     pop();
-  }
-  else {
-    blk = cursp();
+    blk = 1;
   }
   push();pop();
   pop_n(n+1);
@@ -950,39 +1044,40 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
     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);
+      gen_addsub(s, OP_ADD, cursp());
     }
     else if (!noop && symlen == 1 && symname[0] == '-' && n == 1)  {
-      genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
+      gen_addsub(s, OP_SUB, cursp());
     }
     else if (!noop && symlen == 1 && symname[0] == '*' && n == 1)  {
-      genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
+      genop_1(s, OP_MUL, cursp());
     }
     else if (!noop && symlen == 1 && symname[0] == '/' && n == 1)  {
-      genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
+      genop_1(s, OP_DIV, cursp());
     }
     else if (!noop && symlen == 1 && symname[0] == '<' && n == 1)  {
-      genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
+      genop_1(s, OP_LT, cursp());
     }
     else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1)  {
-      genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
+      genop_1(s, OP_LE, cursp());
     }
     else if (!noop && symlen == 1 && symname[0] == '>' && n == 1)  {
-      genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
+      genop_1(s, OP_GT, cursp());
     }
     else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1)  {
-      genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
+      genop_1(s, OP_GE, cursp());
     }
     else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1)  {
-      genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
+      genop_1(s, OP_EQ, cursp());
     }
     else {
-      if (sendv) n = CALL_MAXARGS;
-      if (blk > 0) {                   /* no block */
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
+      int idx = new_sym(s, sym);
+
+      if (sendv) {
+        genop_2(s, blk ? OP_SENDVB : OP_SENDV, cursp(), idx);
       }
       else {
-        genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
+        genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), idx, n);
       }
     }
   }
@@ -1004,13 +1099,15 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
   switch (type) {
   case NODE_GVAR:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
+    genop_2(s, OP_SETGV, sp, idx);
     break;
+  case NODE_ARG:
   case NODE_LVAR:
     idx = lv_idx(s, nsym(tree));
     if (idx > 0) {
       if (idx != sp) {
-        genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
+        gen_move(s, idx, sp, val);
+        if (val && on_eval(s)) genop_0(s, OP_NOP);
       }
       break;
     }
@@ -1021,7 +1118,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
       while (up) {
         idx = lv_idx(up, nsym(tree));
         if (idx > 0) {
-          genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
+          genop_3(s, OP_SETUPVAR, sp, idx, lv);
           break;
         }
         lv++;
@@ -1031,23 +1128,23 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
     break;
   case NODE_IVAR:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
+    genop_2(s, OP_SETIV, sp, idx);
     break;
   case NODE_CVAR:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
+    genop_2(s, OP_SETCV, sp, idx);
     break;
   case NODE_CONST:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
+    genop_2(s, OP_SETCONST, sp, idx);
     break;
   case NODE_COLON2:
-    idx = new_sym(s, nsym(tree->cdr));
-    genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
+    gen_move(s, cursp(), sp, 0);
     push();
     codegen(s, tree->car, VAL);
     pop_n(2);
-    genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
+    idx = new_sym(s, nsym(tree->cdr));
+    genop_2(s, OP_SETMCNST, sp, idx);
     break;
 
   case NODE_CALL:
@@ -1057,7 +1154,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
              type == NODE_SCALL);
     pop();
     if (val) {
-      genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
+      gen_move(s, cursp(), sp, 0);
     }
     break;
 
@@ -1088,8 +1185,12 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
     t = tree->car;
     n = 0;
     while (t) {
-      genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n));
-      gen_assignment(s, t->car, cursp(), NOVAL);
+      int sp = cursp();
+
+      genop_3(s, OP_AREF, sp, rhs, n);
+      push();
+      gen_assignment(s, t->car, sp, NOVAL);
+      pop();
       n++;
       t = t->cdr;
     }
@@ -1103,17 +1204,12 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
         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));
+    gen_move(s, cursp(), rhs, val);
+    push_n(post+1);
+    pop_n(post+1);
+    genop_3(s, OP_APOST, cursp(), n, post);
     n = 1;
-    if (t->car) {               /* rest */
+    if (t->car && t->car != (node*)-1) { /* rest */
       gen_assignment(s, t->car, cursp(), NOVAL);
     }
     if (t->cdr && t->cdr->car) {
@@ -1124,20 +1220,20 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
         n++;
       }
     }
-    if (!val) {
-      push();
+    if (val) {
+      gen_move(s, cursp(), rhs, 0);
     }
   }
 }
 
 static void
-gen_send_intern(codegen_scope *s)
+gen_intern(codegen_scope *s)
 {
-  push();pop(); /* space for a block */
   pop();
-  genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
+  genop_1(s, OP_INTERN, cursp());
   push();
 }
+
 static void
 gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
 {
@@ -1160,25 +1256,25 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
           j = 0;
           ++i;
           if (sym)
-            gen_send_intern(s);
+            gen_intern(s);
         }
         break;
       }
-      if (j >= 2) {
+      while (j >= 2) {
         pop(); pop();
-        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+        genop_1(s, OP_STRCAT, cursp());
         push();
-        j = 1;
+        j--;
       }
       tree = tree->cdr;
     }
     if (j > 0) {
       ++i;
       if (sym)
-        gen_send_intern(s);
+        gen_intern(s);
     }
     pop_n(i);
-    genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
+    genop_2(s, OP_ARRAY, cursp(), i);
     push();
   }
   else {
@@ -1197,7 +1293,7 @@ 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));
+  genop_1(s, OP_ERR, idx);
 }
 
 #ifndef MRB_WITHOUT_FLOAT
@@ -1275,11 +1371,9 @@ static void
 gen_retval(codegen_scope *s, node *tree)
 {
   if (nint(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));
+    pop();
+    genop_1(s, OP_ARYDUP, cursp());
   }
   else {
     codegen(s, tree, VAL);
@@ -1295,7 +1389,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   if (!tree) {
     if (val) {
-      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      genop_1(s, OP_LOADNIL, cursp());
       push();
     }
     return;
@@ -1306,11 +1400,14 @@ codegen(codegen_scope *s, node *tree, int val)
     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);
+    mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+    const char *filename = mrb_sym2name_len(s->mrb, fname, NULL);
+
+    mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
+                               filename, s->lines, 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);
+    s->filename_sym = mrb_parser_get_filename(s->parser, tree->filename_index);
   }
 
   nt = nint(tree->car);
@@ -1319,7 +1416,7 @@ codegen(codegen_scope *s, node *tree, int val)
   switch (nt) {
   case NODE_BEGIN:
     if (val && !tree) {
-      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      genop_1(s, OP_LOADNIL, cursp());
       push();
     }
     while (tree) {
@@ -1330,18 +1427,18 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_RESCUE:
     {
-      int onerr, noexc, exend, pos1, pos2, tmp;
+      int 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;
+      lp->pc0 = new_label(s);
+      lp->pc1 = genjmp(s, OP_ONERR, 0);
       codegen(s, tree->car, VAL);
       pop();
       lp->type = LOOP_RESCUE;
-      noexc = genop(s, MKOP_Bx(OP_JMP, 0));
-      dispatch(s, onerr);
+      noexc = genjmp(s, OP_JMP, 0);
+      dispatch(s, lp->pc1);
       tree = tree->cdr;
       exend = 0;
       pos1 = 0;
@@ -1349,7 +1446,7 @@ codegen(codegen_scope *s, node *tree, int val)
         node *n2 = tree->car;
         int exc = cursp();
 
-        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+        genop_1(s, OP_EXCEPT, exc);
         push();
         while (n2) {
           node *n3 = n2->car;
@@ -1360,30 +1457,29 @@ codegen(codegen_scope *s, node *tree, int val)
           do {
             if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
               codegen(s, n4->car, VAL);
-              genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+              gen_move(s, cursp(), exc, 0);
               push_n(2); pop_n(2); /* space for one arg and a block */
               pop();
-              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+              genop_3(s, OP_SEND, cursp(), new_sym(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"))));
+                genop_2(s, OP_GETCONST, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "StandardError")));
                 push();
               }
               pop();
-              genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
+              genop_2(s, OP_RESCUE, exc, cursp());
             }
-            distcheck(s, pos2);
-            tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+            tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val);
             pos2 = tmp;
             if (n4) {
               n4 = n4->cdr;
             }
           } while (n4);
-          pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+          pos1 = genjmp(s, OP_JMP, 0);
           dispatch_linked(s, pos2);
 
           pop();
@@ -1394,21 +1490,20 @@ codegen(codegen_scope *s, node *tree, int val)
             codegen(s, n3->cdr->cdr->car, val);
             if (val) pop();
           }
-          distcheck(s, exend);
-          tmp = genop(s, MKOP_sBx(OP_JMP, exend));
+          tmp = genjmp(s, OP_JMP, exend);
           exend = tmp;
           n2 = n2->cdr;
           push();
         }
         if (pos1) {
           dispatch(s, pos1);
-          genop(s, MKOP_A(OP_RAISE, exc));
+          genop_1(s, OP_RAISE, exc);
         }
       }
       pop();
       tree = tree->cdr;
       dispatch(s, noexc);
-      genop(s, MKOP_A(OP_POPERR, 1));
+      genop_1(s, OP_POPERR, 1);
       if (tree->car) {
         codegen(s, tree->car, val);
       }
@@ -1425,15 +1520,13 @@ codegen(codegen_scope *s, node *tree, int val)
         (nint(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);
+      genop_1(s, OP_EPUSH, idx);
+      codegen(s, tree->car, val);
       s->ensure_level--;
-      genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
+      genop_1(s, OP_EPOP, 1);
     }
     else {                      /* empty ensure ignored */
       codegen(s, tree->car, val);
@@ -1444,7 +1537,7 @@ codegen(codegen_scope *s, node *tree, int val)
     if (val) {
       int idx = lambda_body(s, tree, 1);
 
-      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
+      genop_2(s, OP_LAMBDA, cursp(), idx);
       push();
     }
     break;
@@ -1453,7 +1546,7 @@ codegen(codegen_scope *s, node *tree, int val)
     if (val) {
       int idx = lambda_body(s, tree, 1);
 
-      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
+      genop_2(s, OP_BLOCK, cursp(), idx);
       push();
     }
     break;
@@ -1461,10 +1554,10 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_IF:
     {
       int pos1, pos2;
-      node *e = tree->cdr->cdr->car;
+      node *elsepart = tree->cdr->cdr->car;
 
       if (!tree->car) {
-        codegen(s, e, val);
+        codegen(s, elsepart, val);
         goto exit;
       }
       switch (nint(tree->car->car)) {
@@ -1475,27 +1568,27 @@ codegen(codegen_scope *s, node *tree, int val)
         goto exit;
       case NODE_FALSE:
       case NODE_NIL:
-        codegen(s, e, val);
+        codegen(s, elsepart, val);
         goto exit;
       }
       codegen(s, tree->car, VAL);
       pop();
-      pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
+      pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
 
       codegen(s, tree->cdr->car, val);
-      if (e) {
+      if (elsepart) {
         if (val) pop();
-        pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+        pos2 = genjmp(s, OP_JMP, 0);
         dispatch(s, pos1);
-        codegen(s, e, val);
+        codegen(s, elsepart, val);
         dispatch(s, pos2);
       }
       else {
         if (val) {
           pop();
-          pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+          pos2 = genjmp(s, OP_JMP, 0);
           dispatch(s, pos1);
-          genop(s, MKOP_A(OP_LOADNIL, cursp()));
+          genop_1(s, OP_LOADNIL, cursp());
           dispatch(s, pos2);
           push();
         }
@@ -1512,7 +1605,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       codegen(s, tree->car, VAL);
       pop();
-      pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+      pos = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
       codegen(s, tree->cdr, val);
       dispatch(s, pos);
     }
@@ -1524,7 +1617,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       codegen(s, tree->car, VAL);
       pop();
-      pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+      pos = genjmp2(s, OP_JMPIF, cursp(), 0, val);
       codegen(s, tree->cdr, val);
       dispatch(s, pos);
     }
@@ -1534,14 +1627,14 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
 
-      lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+      lp->pc0 = new_label(s);
+      lp->pc1 = genjmp(s, OP_JMP, 0);
       lp->pc2 = new_label(s);
       codegen(s, tree->cdr, NOVAL);
       dispatch(s, lp->pc1);
       codegen(s, tree->car, VAL);
       pop();
-      distcheck(s, lp->pc2 - s->pc);
-      genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
+      genjmp2(s, OP_JMPIF, cursp(), lp->pc2, NOVAL);
 
       loop_pop(s, val);
     }
@@ -1551,14 +1644,14 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
 
-      lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+      lp->pc0 = new_label(s);
+      lp->pc1 = genjmp(s, OP_JMP, 0);
       lp->pc2 = new_label(s);
       codegen(s, tree->cdr, NOVAL);
       dispatch(s, lp->pc1);
       codegen(s, tree->car, VAL);
       pop();
-      distcheck(s, lp->pc2 - s->pc);
-      genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
+      genjmp2(s, OP_JMPNOT, cursp(), lp->pc2, NOVAL);
 
       loop_pop(s, val);
     }
@@ -1587,43 +1680,40 @@ codegen(codegen_scope *s, node *tree, int val)
         while (n) {
           codegen(s, n->car, VAL);
           if (head) {
-            genop(s, MKOP_AB(OP_MOVE, cursp(), head));
-            push_n(2); pop_n(2); /* space for one arg and a block */
-            pop();
+            gen_move(s, cursp(), head, 0);
+            push(); push(); pop(); pop(); pop();
             if (nint(n->car->car) == NODE_SPLAT) {
-              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+              genop_3(s, OP_SEND, cursp(), new_sym(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));
+              genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "===")), 1);
             }
           }
           else {
             pop();
           }
-          distcheck(s, pos2);
-          tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+          tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, NOVAL);
           pos2 = tmp;
           n = n->cdr;
         }
         if (tree->car->car) {
-          pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+          pos1 = genjmp(s, OP_JMP, 0);
           dispatch_linked(s, pos2);
         }
         codegen(s, tree->car->cdr, val);
         if (val) pop();
-        distcheck(s, pos3);
-        tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
+        tmp = genjmp(s, 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()));
+        genop_1(s, OP_LOADNIL, cursp());
         if (pos3) dispatch_linked(s, pos3);
         if (head) pop();
         if (cursp() != pos) {
-          genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+          gen_move(s, cursp(), pos, 0);
         }
         push();
       }
@@ -1655,7 +1745,7 @@ codegen(codegen_scope *s, node *tree, int val)
     codegen(s, tree->cdr, val);
     if (val) {
       pop(); pop();
-      genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
+      genop_1(s, OP_RANGE_INC, cursp());
       push();
     }
     break;
@@ -1665,7 +1755,7 @@ codegen(codegen_scope *s, node *tree, int val)
     codegen(s, tree->cdr, val);
     if (val) {
       pop(); pop();
-      genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
+      genop_1(s, OP_RANGE_EXC, cursp());
       push();
     }
     break;
@@ -1676,7 +1766,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       codegen(s, tree->car, VAL);
       pop();
-      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+      genop_2(s, OP_GETMCNST, cursp(), sym);
       if (val) push();
     }
     break;
@@ -1685,8 +1775,8 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_A(OP_OCLASS, cursp()));
-      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+      genop_1(s, OP_OCLASS, cursp());
+      genop_2(s, OP_GETMCNST, cursp(), sym);
       if (val) push();
     }
     break;
@@ -1699,7 +1789,7 @@ codegen(codegen_scope *s, node *tree, int val)
       if (n >= 0) {
         if (val) {
           pop_n(n);
-          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+          genop_2(s, OP_ARRAY, cursp(), n);
           push();
         }
       }
@@ -1710,21 +1800,47 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_HASH:
+  case NODE_KW_HASH:
     {
       int len = 0;
       mrb_bool update = FALSE;
 
       while (tree) {
-        codegen(s, tree->car->car, val);
-        codegen(s, tree->car->cdr, val);
-        len++;
+        if (nint(tree->car->car->car) == NODE_KW_REST_ARGS) {
+          if (len > 0) {
+            pop_n(len*2);
+            if (!update) {
+              genop_2(s, OP_HASH, cursp(), len);
+            }
+            else {
+              pop();
+              genop_2(s, OP_HASHADD, cursp(), len);
+            }
+            push();
+          }
+          codegen(s, tree->car->cdr, VAL);
+          if (len > 0 || update) {
+            pop(); pop();
+            genop_1(s, OP_HASHCAT, cursp());
+            push();
+          }
+          update = TRUE;
+          len = 0;
+        }
+        else {
+          codegen(s, tree->car->car, val);
+          codegen(s, tree->car->cdr, val);
+          len++;
+        }
         tree = tree->cdr;
-        if (val && len == 126) {
+        if (val && len == 255) {
           pop_n(len*2);
-          genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
-          if (update) {
+          if (!update) {
+            genop_2(s, OP_HASH, cursp(), len);
+          }
+          else {
             pop();
-            genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+            genop_2(s, OP_HASHADD, cursp(), len);
           }
           push();
           update = TRUE;
@@ -1733,10 +1849,14 @@ codegen(codegen_scope *s, node *tree, int val)
       }
       if (val) {
         pop_n(len*2);
-        genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
-        if (update) {
+        if (!update) {
+          genop_2(s, OP_HASH, cursp(), len);
+        }
+        else {
           pop();
-          genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+          if (len > 0) {
+            genop_2(s, OP_HASHADD, cursp(), len);
+          }
         }
         push();
       }
@@ -1777,7 +1897,7 @@ codegen(codegen_scope *s, node *tree, int val)
               n++;
             }
             else {
-              genop(s, MKOP_A(OP_LOADNIL, rhs+n));
+              genop_1(s, OP_LOADNIL, rhs+n);
               gen_assignment(s, t->car, rhs+n, NOVAL);
             }
             t = t->cdr;
@@ -1801,7 +1921,7 @@ codegen(codegen_scope *s, node *tree, int val)
             else {
               rn = len - post - n;
             }
-            genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
+            genop_3(s, OP_ARRAY2, cursp(), rhs+n, rn);
             gen_assignment(s, t->car, cursp(), NOVAL);
             n += rn;
           }
@@ -1816,7 +1936,7 @@ codegen(codegen_scope *s, node *tree, int val)
         }
         pop_n(len);
         if (val) {
-          genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
+          genop_2(s, OP_ARRAY, rhs, len);
           push();
         }
       }
@@ -1844,17 +1964,17 @@ codegen(codegen_scope *s, node *tree, int val)
         int onerr, noexc, exc;
         struct loopinfo *lp;
 
-        onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+        onerr = genjmp(s, 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));
+        genop_1(s, OP_POPERR, 1);
+        noexc = genjmp(s, OP_JMP, 0);
         dispatch(s, onerr);
-        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
-        genop(s, MKOP_A(OP_LOADF, exc));
+        genop_1(s, OP_EXCEPT, exc);
+        genop_1(s, OP_LOADF, exc);
         dispatch(s, noexc);
         loop_pop(s, NOVAL);
       }
@@ -1868,7 +1988,7 @@ codegen(codegen_scope *s, node *tree, int val)
           push();
         }
         codegen(s, n->car, VAL);   /* receiver */
-        idx = new_msym(s, nsym(n->cdr->car));
+        idx = new_sym(s, nsym(n->cdr->car));
         base = cursp()-1;
         if (n->cdr->cdr->car) {
           nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
@@ -1882,12 +2002,12 @@ codegen(codegen_scope *s, node *tree, int val)
           }
         }
         /* copy receiver and arguments */
-        genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+        gen_move(s, cursp(), base, 1);
         for (i=0; i<nargs; i++) {
-          genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
+          gen_move(s, cursp()+i+1, base+i+1, 1);
         }
         push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+        genop_3(s, OP_SEND, cursp(), idx, callargs);
         push();
       }
       else {
@@ -1901,30 +2021,30 @@ codegen(codegen_scope *s, node *tree, int val)
         pop();
         if (val) {
           if (vsp >= 0) {
-            genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+            gen_move(s, vsp, cursp(), 1);
           }
-          pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
+          pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
         }
         else {
-          pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
+          pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
         }
         codegen(s, tree->cdr->cdr->car, VAL);
         pop();
         if (val && vsp >= 0) {
-          genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+          gen_move(s, vsp, cursp(), 1);
         }
         if (nint(tree->car->car) == NODE_CALL) {
           if (callargs == CALL_MAXARGS) {
             pop();
-            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            genop_1(s, OP_ARYPUSH, cursp());
           }
           else {
             pop_n(callargs);
             callargs++;
           }
           pop();
-          idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
-          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+          idx = new_sym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
+          genop_3(s, OP_SEND, cursp(), idx, callargs);
         }
         else {
           gen_assignment(s, tree->car, cursp(), val);
@@ -1936,52 +2056,52 @@ codegen(codegen_scope *s, node *tree, int 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);
+        gen_addsub(s, OP_ADD, cursp());
       }
       else if (len == 1 && name[0] == '-')  {
-        genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
+        gen_addsub(s, OP_SUB, cursp());
       }
       else if (len == 1 && name[0] == '*')  {
-        genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
+        genop_1(s, OP_MUL, cursp());
       }
       else if (len == 1 && name[0] == '/')  {
-        genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
+        genop_1(s, OP_DIV, cursp());
       }
       else if (len == 1 && name[0] == '<')  {
-        genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
+        genop_1(s, OP_LT, cursp());
       }
       else if (len == 2 && name[0] == '<' && name[1] == '=')  {
-        genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
+        genop_1(s, OP_LE, cursp());
       }
       else if (len == 1 && name[0] == '>')  {
-        genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
+        genop_1(s, OP_GT, cursp());
       }
       else if (len == 2 && name[0] == '>' && name[1] == '=')  {
-        genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
+        genop_1(s, OP_GE, cursp());
       }
       else {
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
+        idx = new_sym(s, sym);
+        genop_3(s, 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()));
+          gen_move(s, vsp, cursp(), 0);
         }
         if (callargs == CALL_MAXARGS) {
           pop();
-          genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+          genop_1(s, OP_ARYPUSH, cursp());
         }
         else {
           pop_n(callargs);
           callargs++;
         }
         pop();
-        idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+        idx = new_sym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
+        genop_3(s, OP_SEND, cursp(), idx, callargs);
       }
     }
     break;
@@ -1998,7 +2118,7 @@ codegen(codegen_scope *s, node *tree, int val)
         s2 = s2->prev;
         if (!s2) break;
       }
-      genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
+      genop_2S(s, OP_ARGARY, cursp(), (lv & 0xf));
       push(); push();         /* ARGARY pushes two values */
       pop(); pop();
       if (tree) {
@@ -2016,12 +2136,12 @@ codegen(codegen_scope *s, node *tree, int val)
         pop();
       }
       else {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         push(); pop();
       }
       pop_n(n+1);
       if (sendv) n = CALL_MAXARGS;
-      genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
+      genop_2(s, OP_SUPER, cursp(), n);
       if (val) push();
     }
     break;
@@ -2038,14 +2158,14 @@ codegen(codegen_scope *s, node *tree, int val)
         if (!s2) break;
       }
       if (s2) ainfo = s2->ainfo;
-      genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
+      genop_2S(s, 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));
+      genop_2(s, OP_SUPER, cursp(), CALL_MAXARGS);
       if (val) push();
     }
     break;
@@ -2055,13 +2175,13 @@ codegen(codegen_scope *s, node *tree, int val)
       gen_retval(s, tree);
     }
     else {
-      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      genop_1(s, OP_LOADNIL, cursp());
     }
     if (s->loop) {
-      genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
+      gen_return(s, OP_RETURN_BLK, cursp());
     }
     else {
-      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+      gen_return(s, OP_RETURN, cursp());
     }
     if (val) push();
     break;
@@ -2088,9 +2208,9 @@ codegen(codegen_scope *s, node *tree, int val)
       }
       push();pop(); /* space for a block */
       pop_n(n+1);
-      genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
+      genop_2S(s, 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));
+      genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "call")), n);
       if (val) push();
     }
     break;
@@ -2106,11 +2226,10 @@ codegen(codegen_scope *s, node *tree, int val)
     }
     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);
+        genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
       }
       codegen(s, tree, NOVAL);
-      distcheck(s, s->loop->pc1 - s->pc);
-      genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
+      genjmp(s, OP_JMP, s->loop->pc0);
     }
     else {
       if (tree) {
@@ -2118,9 +2237,9 @@ codegen(codegen_scope *s, node *tree, int val)
         pop();
       }
       else {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
       }
-      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+      gen_return(s, OP_RETURN, cursp());
     }
     if (val) push();
     break;
@@ -2131,10 +2250,9 @@ codegen(codegen_scope *s, node *tree, int val)
     }
     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_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
       }
-      distcheck(s, s->loop->pc2 - s->pc);
-      genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
+      genjmp(s, OP_JMP, s->loop->pc2);
     }
     if (val) push();
     break;
@@ -2161,13 +2279,12 @@ codegen(codegen_scope *s, node *tree, int val)
         }
         else {
           if (n > 0) {
-            genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+            genop_1(s, OP_POPERR, n);
           }
           if (s->ensure_level > lp->ensure_level) {
-            genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
+            genop_1(s, OP_EPOP, s->ensure_level - lp->ensure_level);
           }
-          distcheck(s, s->loop->pc1 - s->pc);
-          genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+          genjmp(s, OP_JMP, lp->pc0);
         }
       }
       if (val) push();
@@ -2179,7 +2296,8 @@ codegen(codegen_scope *s, node *tree, int val)
       int idx = lv_idx(s, nsym(tree));
 
       if (idx > 0) {
-        genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
+        gen_move(s, cursp(), idx, val);
+        if (val && on_eval(s)) genop_0(s, OP_NOP);
       }
       else {
         int lv = 0;
@@ -2188,7 +2306,7 @@ codegen(codegen_scope *s, node *tree, int val)
         while (up) {
           idx = lv_idx(up, nsym(tree));
           if (idx > 0) {
-            genop_peep(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv), VAL);
+            genop_3(s, OP_GETUPVAR, cursp(), idx, lv);
             break;
           }
           lv++;
@@ -2200,29 +2318,29 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_GVAR:
-    if (val) {
+    {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
-      push();
+      genop_2(s, OP_GETGV, cursp(), sym);
+      if (val) push();
     }
     break;
 
   case NODE_IVAR:
-    if (val) {
+    {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
-      push();
+      genop_2(s, OP_GETIV, cursp(), sym);
+      if (val) push();
     }
     break;
 
   case NODE_CVAR:
-    if (val) {
+    {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
-      push();
+      genop_2(s, OP_GETCV, cursp(), sym);
+      if (val) push();
     }
     break;
 
@@ -2230,27 +2348,21 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
-      if (val) {
-        push();
-      }
+      genop_2(s, OP_GETCONST, cursp(), sym);
+      if (val) push();
     }
     break;
 
   case NODE_DEFINED:
-    codegen(s, tree, VAL);
+    codegen(s, tree, val);
     break;
 
   case NODE_BACK_REF:
     if (val) {
-      char buf[3];
-      int sym;
+      char buf[] = {'$', nchar(tree)};
+      int sym = new_sym(s, mrb_intern(s->mrb, buf, sizeof(buf)));
 
-      buf[0] = '$';
-      buf[1] = nchar(tree);
-      buf[2] = 0;
-      sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
-      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+      genop_2(s, OP_GETGV, cursp(), sym);
       push();
     }
     break;
@@ -2263,7 +2375,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
       sym = new_sym(s, mrb_intern_str(mrb, str));
-      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+      genop_2(s, OP_GETGV, cursp(), sym);
       push();
     }
     break;
@@ -2273,7 +2385,7 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_BLOCK_ARG:
-    codegen(s, tree, VAL);
+    codegen(s, tree, val);
     break;
 
   case NODE_INT:
@@ -2281,7 +2393,6 @@ codegen(codegen_scope *s, node *tree, int val)
       char *p = (char*)tree->car;
       int base = nint(tree->cdr->car);
       mrb_int i;
-      mrb_code co;
       mrb_bool overflow;
 
       i = readint_mrb_int(s, p, base, FALSE, &overflow);
@@ -2290,19 +2401,19 @@ codegen(codegen_scope *s, node *tree, int val)
         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));
+        genop_2(s, OP_LOADL, cursp(), off);
       }
       else
 #endif
       {
-        if (i < MAXARG_sBx && i > -MAXARG_sBx) {
-          co = MKOP_AsBx(OP_LOADI, cursp(), i);
-        }
+        if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+        else if (i < 0) genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
+        else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, cursp());
+        else if (i <= 0xffff) genop_2(s, OP_LOADI, cursp(), (uint16_t)i);
         else {
           int off = new_lit(s, mrb_fixnum_value(i));
-          co = MKOP_ABx(OP_LOADL, cursp(), off);
+          genop_2(s, OP_LOADL, cursp(), off);
         }
-        genop(s, co);
       }
       push();
     }
@@ -2315,7 +2426,7 @@ codegen(codegen_scope *s, node *tree, int val)
       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));
+      genop_2(s, OP_LOADL, cursp(), off);
       push();
     }
     break;
@@ -2324,16 +2435,15 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_NEGATE:
     {
       nt = nint(tree->car);
-      tree = tree->cdr;
       switch (nt) {
 #ifndef MRB_WITHOUT_FLOAT
       case NODE_FLOAT:
         if (val) {
-          char *p = (char*)tree;
+          char *p = (char*)tree->cdr;
           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));
+          genop_2(s, OP_LOADL, cursp(), off);
           push();
         }
         break;
@@ -2341,10 +2451,9 @@ codegen(codegen_scope *s, node *tree, int val)
 
       case NODE_INT:
         if (val) {
-          char *p = (char*)tree->car;
-          int base = nint(tree->cdr->car);
+          char *p = (char*)tree->cdr->car;
+          int base = nint(tree->cdr->cdr->car);
           mrb_int i;
-          mrb_code co;
           mrb_bool overflow;
 
           i = readint_mrb_int(s, p, base, TRUE, &overflow);
@@ -2353,18 +2462,18 @@ codegen(codegen_scope *s, node *tree, int val)
             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));
+            genop_2(s, OP_LOADL, cursp(), off);
           }
           else {
 #endif
-            if (i < MAXARG_sBx && i > -MAXARG_sBx) {
-              co = MKOP_AsBx(OP_LOADI, cursp(), i);
+            if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+            else if (i >= -0xffff) {
+              genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
             }
             else {
               int off = new_lit(s, mrb_fixnum_value(i));
-              co = MKOP_ABx(OP_LOADL, cursp(), off);
+              genop_2(s, OP_LOADL, cursp(), off);
             }
-            genop(s, co);
 #ifndef MRB_WITHOUT_FLOAT
           }
 #endif
@@ -2374,13 +2483,11 @@ codegen(codegen_scope *s, node *tree, int val)
 
       default:
         if (val) {
-          int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
-
-          genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
-          push();
+          int sym = new_sym(s, mrb_intern_lit(s->mrb, "-@"));
           codegen(s, tree, VAL);
-          pop(); pop();
-          genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
+          pop();
+          genop_3(s, OP_SEND, cursp(), sym, 0);
+          push();
         }
         else {
           codegen(s, tree, NOVAL);
@@ -2398,7 +2505,7 @@ codegen(codegen_scope *s, node *tree, int val)
       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));
+      genop_2(s, OP_STRING, cursp(), off);
       push();
     }
     break;
@@ -2411,7 +2518,7 @@ codegen(codegen_scope *s, node *tree, int val)
       node *n = tree;
 
       if (!n) {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         push();
         break;
       }
@@ -2420,7 +2527,7 @@ codegen(codegen_scope *s, node *tree, int val)
       while (n) {
         codegen(s, n->car, VAL);
         pop(); pop();
-        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+        genop_1(s, OP_STRCAT, cursp());
         push();
         n = n->cdr;
       }
@@ -2451,7 +2558,7 @@ codegen(codegen_scope *s, node *tree, int val)
       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()));
+      genop_1(s, OP_LOADSELF, cursp());
       push();
       codegen(s, tree->car, VAL);
       n = tree->cdr;
@@ -2462,14 +2569,14 @@ codegen(codegen_scope *s, node *tree, int val)
         }
         codegen(s, n->car, VAL);
         pop(); pop();
-        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+        genop_1(s, OP_STRCAT, cursp());
         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));
+      genop_3(s, OP_SEND, cursp(), sym, 1);
       if (val) push();
       mrb_gc_arena_restore(s->mrb, ai);
     }
@@ -2483,13 +2590,13 @@ codegen(codegen_scope *s, node *tree, int val)
       int off = new_lit(s, mrb_str_new(s->mrb, p, len));
       int sym;
 
-      genop(s, MKOP_A(OP_LOADSELF, cursp()));
+      genop_1(s, OP_LOADSELF, cursp());
       push();
-      genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+      genop_2(s, 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));
+      genop_3(s, OP_SEND, cursp(), sym, 1);
       if (val) push();
       mrb_gc_arena_restore(s->mrb, ai);
     }
@@ -2505,24 +2612,24 @@ codegen(codegen_scope *s, node *tree, int val)
       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));
+      genop_1(s, OP_OCLASS, cursp());
+      genop_2(s, OP_GETMCNST, cursp(), sym);
       push();
-      genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+      genop_2(s, OP_STRING, cursp(), off);
       push();
       if (p2 || p3) {
         if (p2) { /* opt */
           off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
-          genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+          genop_2(s, OP_STRING, cursp(), off);
         }
         else {
-          genop(s, MKOP_A(OP_LOADNIL, cursp()));
+          genop_1(s, OP_LOADNIL, cursp());
         }
         push();
         argc++;
         if (p3) { /* enc */
           off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
-          genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+          genop_2(s, OP_STRING, cursp(), off);
           push();
           argc++;
         }
@@ -2530,7 +2637,7 @@ codegen(codegen_scope *s, node *tree, int val)
       push(); /* space for a block */
       pop_n(argc+2);
       sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
-      genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+      genop_3(s, OP_SEND, cursp(), sym, argc);
       mrb_gc_arena_restore(s->mrb, ai);
       push();
     }
@@ -2545,15 +2652,15 @@ codegen(codegen_scope *s, node *tree, int val)
       int off;
       char *p;
 
-      genop(s, MKOP_A(OP_OCLASS, cursp()));
-      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+      genop_1(s, OP_OCLASS, cursp());
+      genop_2(s, 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);
+        genop_1(s, OP_STRCAT, cursp());
         push();
         n = n->cdr;
       }
@@ -2562,29 +2669,29 @@ codegen(codegen_scope *s, node *tree, int val)
         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));
+        genop_2(s, OP_STRING, cursp(), off);
         pop();
-        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+        genop_1(s, OP_STRCAT, cursp());
         push();
       }
       if (n->cdr->car) { /* opt */
         char *p2 = (char*)n->cdr->car;
         off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
-        genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+        genop_2(s, OP_STRING, cursp(), off);
         push();
         argc++;
       }
       if (n->cdr->cdr) { /* enc */
         char *p2 = (char*)n->cdr->cdr;
         off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
-        genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+        genop_2(s, OP_STRING, cursp(), off);
         push();
         argc++;
       }
       push(); /* space for a block */
       pop_n(argc+2);
       sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
-      genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+      genop_3(s, OP_SEND, cursp(), sym, argc);
       mrb_gc_arena_restore(s->mrb, ai);
       push();
     }
@@ -2604,7 +2711,7 @@ codegen(codegen_scope *s, node *tree, int val)
     if (val) {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+      genop_2(s, OP_LOADSYM, cursp(), sym);
       push();
     }
     break;
@@ -2612,55 +2719,46 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_DSYM:
     codegen(s, tree, val);
     if (val) {
-      gen_send_intern(s);
+      gen_intern(s);
     }
     break;
 
   case NODE_SELF:
     if (val) {
-      genop(s, MKOP_A(OP_LOADSELF, cursp()));
+      genop_1(s, OP_LOADSELF, cursp());
       push();
     }
     break;
 
   case NODE_NIL:
     if (val) {
-      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      genop_1(s, OP_LOADNIL, cursp());
       push();
     }
     break;
 
   case NODE_TRUE:
     if (val) {
-      genop(s, MKOP_A(OP_LOADT, cursp()));
+      genop_1(s, OP_LOADT, cursp());
       push();
     }
     break;
 
   case NODE_FALSE:
     if (val) {
-      genop(s, MKOP_A(OP_LOADF, cursp()));
+      genop_1(s, OP_LOADF, cursp());
       push();
     }
     break;
 
   case NODE_ALIAS:
     {
-      int a = new_msym(s, nsym(tree->car));
-      int b = new_msym(s, nsym(tree->cdr));
-      int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
+      int a = new_sym(s, nsym(tree->car));
+      int b = new_sym(s, nsym(tree->cdr));
 
-      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(); /* space for a block */
-      pop_n(4);
-      genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
+      genop_2(s, OP_ALIAS, a, b);
       if (val) {
+        genop_1(s, OP_LOADNIL, cursp());
         push();
       }
     }
@@ -2668,41 +2766,15 @@ codegen(codegen_scope *s, node *tree, int val)
 
   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, nsym(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, nsym(t->car));
-        genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
-        push();
+        int symbol = new_sym(s, nsym(t->car));
+        genop_1(s, OP_UNDEF, symbol);
         t = t->cdr;
-        num++;
-      }
-      push();pop(); /* space for a block */
-      pop();
-      if (num < CALL_MAXARGS) {
-        pop_n(num);
       }
-      genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
       if (val) {
+        genop_1(s, OP_LOADNIL, cursp());
         push();
       }
     }
@@ -2711,13 +2783,14 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_CLASS:
     {
       int idx;
+      node *body;
 
       if (tree->car->car == (node*)0) {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         push();
       }
       else if (tree->car->car == (node*)1) {
-        genop(s, MKOP_A(OP_OCLASS, cursp()));
+        genop_1(s, OP_OCLASS, cursp());
         push();
       }
       else {
@@ -2727,14 +2800,20 @@ codegen(codegen_scope *s, node *tree, int val)
         codegen(s, tree->cdr->car, VAL);
       }
       else {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         push();
       }
       pop(); pop();
-      idx = new_msym(s, nsym(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));
+      idx = new_sym(s, nsym(tree->car->cdr));
+      genop_2(s, OP_CLASS, cursp(), idx);
+      body = tree->cdr->cdr->car;
+      if (nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL) {
+        genop_1(s, OP_LOADNIL, cursp());
+      }
+      else {
+        idx = scope_body(s, body, val);
+        genop_2(s, OP_EXEC, cursp(), idx);
+      }
       if (val) {
         push();
       }
@@ -2746,21 +2825,27 @@ codegen(codegen_scope *s, node *tree, int val)
       int idx;
 
       if (tree->car->car == (node*)0) {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         push();
       }
       else if (tree->car->car == (node*)1) {
-        genop(s, MKOP_A(OP_OCLASS, cursp()));
+        genop_1(s, OP_OCLASS, cursp());
         push();
       }
       else {
         codegen(s, tree->car->car, VAL);
       }
       pop();
-      idx = new_msym(s, nsym(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));
+      idx = new_sym(s, nsym(tree->car->cdr));
+      genop_2(s, OP_MODULE, cursp(), idx);
+      if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+          tree->cdr->car->cdr->cdr == NULL) {
+        genop_1(s, OP_LOADNIL, cursp());
+      }
+      else {
+        idx = scope_body(s, tree->cdr->car, val);
+        genop_2(s, OP_EXEC, cursp(), idx);
+      }
       if (val) {
         push();
       }
@@ -2773,9 +2858,15 @@ codegen(codegen_scope *s, node *tree, int val)
 
       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));
+      genop_1(s, OP_SCLASS, cursp());
+      if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+          tree->cdr->car->cdr->cdr == NULL) {
+        genop_1(s, OP_LOADNIL, cursp());
+      }
+      else {
+        idx = scope_body(s, tree->cdr->car, val);
+        genop_2(s, OP_EXEC, cursp(), idx);
+      }
       if (val) {
         push();
       }
@@ -2784,17 +2875,17 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_DEF:
     {
-      int sym = new_msym(s, nsym(tree->car));
+      int sym = new_sym(s, nsym(tree->car));
       int idx = lambda_body(s, tree->cdr, 0);
 
-      genop(s, MKOP_A(OP_TCLASS, cursp()));
+      genop_1(s, OP_TCLASS, cursp());
       push();
-      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+      genop_2(s, OP_METHOD, cursp(), idx);
       push(); pop();
       pop();
-      genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+      genop_2(s, OP_DEF, cursp(), sym);
       if (val) {
-        genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+        genop_2(s, OP_LOADSYM, cursp(), sym);
         push();
       }
     }
@@ -2803,18 +2894,18 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_SDEF:
     {
       node *recv = tree->car;
-      int sym = new_msym(s, nsym(tree->cdr->car));
+      int sym = new_sym(s, nsym(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()));
+      genop_1(s, OP_SCLASS, cursp());
       push();
-      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+      genop_2(s, OP_METHOD, cursp(), idx);
       pop();
-      genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+      genop_2(s, OP_DEF, cursp(), sym);
       if (val) {
-        genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+        genop_2(s, OP_LOADSYM, cursp(), sym);
         push();
       }
     }
@@ -2876,7 +2967,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
   p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
   p->irep->plen = 0;
 
-  p->scapa = MAXMSYMLEN;
+  p->scapa = 256;
   p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
   p->irep->slen = 0;
 
@@ -2901,18 +2992,16 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
   }
   p->ai = mrb_gc_arena_save(mrb);
 
-  p->filename = prev->filename;
-  if (p->filename) {
+  p->filename_sym = prev->filename_sym;
+  if (p->filename_sym) {
     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) {
+  if (p->filename_sym) {
     mrb_debug_info_alloc(mrb, p->irep);
-    p->irep->filename = p->filename;
-    p->irep->lines = p->lines;
   }
   else {
     p->irep->debug_info = NULL;
@@ -2930,34 +3019,23 @@ 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);
+  if (s->filename_sym) {
+    mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+    const char *filename = mrb_sym2name_len(s->mrb, fname, NULL);
 
-    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;
+    mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
+                               filename, s->lines, s->debug_start_pos, s->pc);
   }
+  mrb_free(s->mrb, s->lines);
 
   irep->nlocals = s->nlocals;
   irep->nregs = s->nregs;
@@ -2972,7 +3050,7 @@ 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->pc0 = p->pc1 = p->pc2 = p->pc3 = 0;
   p->prev = s->loop;
   p->ensure_level = s->ensure_level;
   p->acc = cursp();
@@ -3014,27 +3092,26 @@ loop_break(codegen_scope *s, node *tree)
       return;
     }
     if (n > 0) {
-      genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+      genop_1(s, OP_POPERR, n);
     }
 
     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);
+        genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
       }
       if (tree) {
-        genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
+        gen_move(s, loop->acc, cursp(), 0);
       }
-      distcheck(s, s->loop->pc3);
-      tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
+      tmp = genjmp(s, OP_JMP, loop->pc3);
       loop->pc3 = tmp;
     }
     else {
       if (!tree) {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
       }
-      genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
+      gen_return(s, OP_BREAK, cursp());
     }
   }
 }
@@ -3043,15 +3120,15 @@ static void
 loop_pop(codegen_scope *s, int val)
 {
   if (val) {
-    genop(s, MKOP_A(OP_LOADNIL, cursp()));
+    genop_1(s, 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)
+static struct RProc*
+generate_code(mrb_state *mrb, parser_state *p, int val)
 {
   codegen_scope *scope = scope_new(mrb, 0, 0);
   struct RProc *proc;
@@ -3062,13 +3139,13 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
   }
   scope->mrb = mrb;
   scope->parser = p;
-  scope->filename = p->filename;
+  scope->filename_sym = p->filename_sym;
   scope->filename_index = p->current_filename_index;
 
   MRB_TRY(&scope->jmp) {
-    mrb->jmp = &scope->jmp; 
+    mrb->jmp = &scope->jmp;
     /* prepare irep */
-    codegen(scope, p->tree, NOVAL);
+    codegen(scope, p->tree, val);
     proc = mrb_proc_new(mrb, scope->irep);
     mrb_irep_decref(mrb, scope->irep);
     mrb_pool_close(scope->mpool);
@@ -3087,3 +3164,77 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
   }
   MRB_END_EXC(&scope->jmp);
 }
+
+MRB_API struct RProc*
+mrb_generate_code(mrb_state *mrb, parser_state *p)
+{
+  return generate_code(mrb, p, VAL);
+}
+
+void
+mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
+{
+  int i;
+
+  if (irep->lv) {
+    mrb_free(mrb, irep->lv);
+    irep->lv = NULL;
+  }
+
+  for (i = 0; i < irep->rlen; ++i) {
+    mrb_irep_remove_lv(mrb, irep->reps[i]);
+  }
+}
+
+#undef OPCODE
+#define Z 1
+#define S 3
+#define W 4
+/* instruction sizes */
+uint8_t mrb_insn_size[] = {
+#define B 2
+#define BB 3
+#define BBB 4
+#define BS 4
+#define SB 4
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef B
+#undef BB
+#undef BS
+#undef SB
+#undef BBB
+};
+/* EXT1 instruction sizes */
+uint8_t mrb_insn_size1[] = {
+#define B 3
+#define BB 4
+#define BBB 5
+#define BS 5
+#define SB 5
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef B
+};
+/* EXT2 instruction sizes */
+uint8_t mrb_insn_size2[] = {
+#define B 2
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef BB
+#undef BBB
+#undef BS
+#undef SB
+};
+/* EXT3 instruction sizes */
+#define BB 5
+#define BBB 6
+#define BS 4
+#define SB 5
+uint8_t mrb_insn_size3[] = {
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+};
index 9636dd7..219bdda 100644 (file)
@@ -9,14 +9,11 @@
 
 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,
@@ -40,12 +37,12 @@ enum node_type {
   NODE_CALL,
   NODE_SCALL,
   NODE_FCALL,
-  NODE_VCALL,
   NODE_SUPER,
   NODE_ZSUPER,
   NODE_ARRAY,
   NODE_ZARRAY,
   NODE_HASH,
+  NODE_KW_HASH,
   NODE_RETURN,
   NODE_YIELD,
   NODE_LVAR,
@@ -57,8 +54,6 @@ enum node_type {
   NODE_NTH_REF,
   NODE_BACK_REF,
   NODE_MATCH,
-  NODE_MATCH2,
-  NODE_MATCH3,
   NODE_INT,
   NODE_FLOAT,
   NODE_NEGATE,
@@ -71,10 +66,10 @@ enum node_type {
   NODE_REGX,
   NODE_DREGX,
   NODE_DREGX_ONCE,
-  NODE_LIST,
   NODE_ARG,
-  NODE_ARGSCAT,
-  NODE_ARGSPUSH,
+  NODE_ARGS_TAIL,
+  NODE_KW_ARG,
+  NODE_KW_REST_ARGS,
   NODE_SPLAT,
   NODE_TO_ARY,
   NODE_SVALUE,
@@ -88,26 +83,15 @@ enum node_type {
   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,
index 7f848b4..88d9ea4 100644 (file)
@@ -133,6 +133,10 @@ cons_gen(parser_state *p, node *car, node *cdr)
   c->cdr = cdr;
   c->lineno = p->lineno;
   c->filename_index = p->current_filename_index;
+  /* beginning of next partial file; need to point the previous file */
+  if (p->lineno == 0 && p->current_filename_index > 0) {
+    c->filename_index-- ;
+  }
   return c;
 }
 #define cons(a,b) cons_gen(p,(a),(b))
@@ -279,6 +283,20 @@ local_add(parser_state *p, mrb_sym sym)
   }
 }
 
+static void
+local_add_blk(parser_state *p, mrb_sym blk)
+{
+  /* allocate register for block */
+  local_add_f(p, blk ? blk : mrb_intern_lit(p->mrb, "&"));
+}
+
+static void
+local_add_kw(parser_state *p, mrb_sym kwd)
+{
+  /* allocate register for keywords hash */
+  local_add_f(p, kwd ? kwd : mrb_intern_lit(p->mrb, "**"));
+}
+
 static node*
 locals_node(parser_state *p)
 {
@@ -568,6 +586,13 @@ new_hash(parser_state *p, node *a)
   return cons((node*)NODE_HASH, a);
 }
 
+/* (:kw_hash (k . v) (k . v)...) */
+static node*
+new_kw_hash(parser_state *p, node *a)
+{
+  return cons((node*)NODE_KW_HASH, a);
+}
+
 /* (:sym . a) */
 static node*
 new_sym(parser_state *p, mrb_sym sym)
@@ -671,23 +696,80 @@ new_arg(parser_state *p, mrb_sym sym)
   return cons((node*)NODE_ARG, nsym(sym));
 }
 
-/* (m o r m2 b) */
+static void
+local_add_margs(parser_state *p, node *n)
+{
+  while (n) {
+    if (n->car->car == (node*)NODE_MASGN) {
+      node *t = n->car->cdr->cdr;
+
+      n->car->cdr->cdr = NULL;
+      while (t) {
+        local_add_f(p, sym(t->car));
+        t = t->cdr;
+      }
+      local_add_margs(p, n->car->cdr->car->car);
+      local_add_margs(p, n->car->cdr->car->cdr->cdr->car);
+    }
+    n = n->cdr;
+  }
+}
+
+/* (m o r m2 tail) */
 /* 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)
+new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail)
 {
   node *n;
 
-  n = cons(m2, nsym(blk));
+  local_add_margs(p, m);
+  local_add_margs(p, m2);
+  n = cons(m2, tail);
   n = cons(nsym(rest), n);
   n = cons(opt, n);
   return cons(m, n);
 }
 
+/* (:args_tail keywords rest_keywords_sym block_sym) */
+static node*
+new_args_tail(parser_state *p, node *kws, node *kwrest, mrb_sym blk)
+{
+  node *k;
+
+  if (kws || kwrest) {
+    local_add_kw(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : 0);
+  }
+
+  local_add_blk(p, blk);
+
+  // allocate register for keywords arguments
+  // order is for Proc#parameters
+  for (k = kws; k; k = k->cdr) {
+    if (!k->car->cdr->cdr->car) { // allocate required keywords
+      local_add_f(p, sym(k->car->cdr->car));
+    }
+  }
+  for (k = kws; k; k = k->cdr) {
+    if (k->car->cdr->cdr->car) { // allocate keywords with default
+      local_add_f(p, sym(k->car->cdr->car));
+    }
+  }
+
+  return list4((node*)NODE_ARGS_TAIL, kws, kwrest, nsym(blk));
+}
+
+/* (:kw_arg kw_sym def_arg) */
+static node*
+new_kw_arg(parser_state *p, mrb_sym kw, node *def_arg)
+{
+  mrb_assert(kw);
+  return list3((node*)NODE_KW_ARG, nsym(kw), def_arg);
+}
+
 /* (:block_arg . a) */
 static node*
 new_block_arg(parser_state *p, node *a)
@@ -725,6 +807,13 @@ new_masgn(parser_state *p, node *a, node *b)
   return cons((node*)NODE_MASGN, cons(a, b));
 }
 
+/* (:masgn mlhs mrhs) no check */
+static node*
+new_masgn_param(parser_state *p, node *a, node *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)
@@ -763,6 +852,87 @@ new_dstr(parser_state *p, node *a)
   return cons((node*)NODE_DSTR, a);
 }
 
+static int
+string_node_p(node *n)
+{
+  return (int)((enum node_type)(intptr_t)n->car == NODE_STR);
+}
+
+static node*
+composite_string_node(parser_state *p, node *a, node *b)
+{
+  size_t newlen = (size_t)a->cdr + (size_t)b->cdr;
+  char *str = (char*)mrb_pool_realloc(p->pool, a->car, (size_t)a->cdr + 1, newlen + 1);
+  memcpy(str + (size_t)a->cdr, b->car, (size_t)b->cdr);
+  str[newlen] = '\0';
+  a->car = (node*)str;
+  a->cdr = (node*)newlen;
+  cons_free(b);
+  return a;
+}
+
+static node*
+concat_string(parser_state *p, node *a, node *b)
+{
+  if (string_node_p(a)) {
+    if (string_node_p(b)) {
+      /* a == NODE_STR && b == NODE_STR */
+      composite_string_node(p, a->cdr, b->cdr);
+      cons_free(b);
+      return a;
+    }
+    else {
+      /* a == NODE_STR && b == NODE_DSTR */
+
+      if (string_node_p(b->cdr->car)) {
+        /* a == NODE_STR && b->[NODE_STR, ...] */
+        composite_string_node(p, a->cdr, b->cdr->car->cdr);
+        cons_free(b->cdr->car);
+        b->cdr->car = a;
+        return b;
+      }
+    }
+  }
+  else if (string_node_p(b)) {
+    /* a == NODE_DSTR && b == NODE_STR */
+
+    node *c;
+    for (c = a; c->cdr != NULL; c = c->cdr) ;
+    if (string_node_p(c->car)) {
+      /* a->[..., NODE_STR] && b == NODE_STR */
+      composite_string_node(p, c->car->cdr, b->cdr);
+      cons_free(b);
+      return a;
+    }
+
+    push(a, b);
+    return a;
+  }
+  else {
+    /* a == NODE_DSTR && b == NODE_DSTR */
+
+    node *c, *d;
+    for (c = a; c->cdr != NULL; c = c->cdr) ;
+    if (string_node_p(c->car) && string_node_p(b->cdr->car)) {
+      /* a->[..., NODE_STR] && b->[NODE_STR, ...] */
+      d = b->cdr;
+      cons_free(b);
+      composite_string_node(p, c->car->cdr, d->car->cdr);
+      cons_free(d->car);
+      c->cdr = d->cdr;
+      cons_free(d);
+      return a;
+    }
+    else {
+      c->cdr = b->cdr;
+      cons_free(b);
+      return a;
+    }
+  }
+
+  return new_dstr(p, list2(a, b));
+}
+
 /* (:str . (s . len)) */
 static node*
 new_xstr(parser_state *p, const char *s, int len)
@@ -1029,7 +1199,6 @@ heredoc_end(parser_state *p)
     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 */
@@ -1112,7 +1281,7 @@ heredoc_end(parser_state *p)
 %token <nd>  tNTH_REF tBACK_REF
 %token <num> tREGEXP_END
 
-%type <nd> singleton string string_rep string_interp xstring regexp
+%type <nd> singleton string string_fragment 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
@@ -1123,7 +1292,7 @@ heredoc_end(parser_state *p)
 %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> f_arglist f_args f_arg f_arg_item f_optarg 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
@@ -1134,6 +1303,10 @@ heredoc_end(parser_state *p)
 %type <nd> heredoc words symbols
 %type <num> call_op call_op2     /* 0:'&.', 1:'.', 2:'::' */
 
+%type <nd> args_tail opt_args_tail f_kwarg f_kw f_kwrest
+%type <nd> f_block_kwarg f_block_kw block_args_tail opt_block_args_tail
+%type <id> f_label
+
 %token tUPLUS             /* unary+ */
 %token tUMINUS            /* unary- */
 %token tPOW               /* ** */
@@ -1159,6 +1332,7 @@ heredoc_end(parser_state *p)
 %token tLBRACE            /* { */
 %token tLBRACE_ARG        /* { */
 %token tSTAR              /* * */
+%token tDSTAR             /* ** */
 %token tAMPER             /* & */
 %token tLAMBDA            /* -> */
 %token tANDDOT            /* &. */
@@ -1736,6 +1910,7 @@ op              : '|'           { $$ = intern_c('|');   }
                 | '/'           { $$ = intern_c('/');   }
                 | '%'           { $$ = intern_c('%');   }
                 | tPOW          { $$ = intern("**",2);  }
+                | tDSTAR        { $$ = intern("**",2);  }
                 | '!'           { $$ = intern_c('!');   }
                 | '~'           { $$ = intern_c('~');   }
                 | tUPLUS        { $$ = intern("+@",2);  }
@@ -1944,11 +2119,11 @@ aref_args       : none
                     }
                 | args comma assocs trailer
                     {
-                      $$ = push($1, new_hash(p, $3));
+                      $$ = push($1, new_kw_hash(p, $3));
                     }
                 | assocs trailer
                     {
-                      $$ = cons(new_hash(p, $1), 0);
+                      $$ = cons(new_kw_hash(p, $1), 0);
                       NODE_LINENO($$, $1);
                     }
                 ;
@@ -1984,12 +2159,12 @@ opt_call_args   : none
                     }
                 | args comma assocs ','
                     {
-                      $$ = cons(push($1, new_hash(p, $3)), 0);
+                      $$ = cons(push($1, new_kw_hash(p, $3)), 0);
                       NODE_LINENO($$, $1);
                     }
                 | assocs ','
                     {
-                      $$ = cons(list1(new_hash(p, $1)), 0);
+                      $$ = cons(list1(new_kw_hash(p, $1)), 0);
                       NODE_LINENO($$, $1);
                     }
                 ;
@@ -2007,12 +2182,12 @@ call_args       : command
                     }
                 | assocs opt_block_arg
                     {
-                      $$ = cons(list1(new_hash(p, $1)), $2);
+                      $$ = cons(list1(new_kw_hash(p, $1)), $2);
                       NODE_LINENO($$, $1);
                     }
                 | args comma assocs opt_block_arg
                     {
-                      $$ = cons(push($1, new_hash(p, $3)), $4);
+                      $$ = cons(push($1, new_kw_hash(p, $3)), $4);
                       NODE_LINENO($$, $1);
                     }
                 | block_arg
@@ -2393,43 +2568,24 @@ for_var         : lhs
                 | 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
+f_margs         : f_arg
                     {
                       $$ = list3($1,0,0);
                     }
-                | f_marg_list ',' tSTAR f_norm_arg
+                | f_arg ',' tSTAR f_norm_arg
                     {
                       $$ = list3($1, new_arg(p, $4), 0);
                     }
-                | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
+                | f_arg ',' tSTAR f_norm_arg ',' f_arg
                     {
                       $$ = list3($1, new_arg(p, $4), $6);
                     }
-                | f_marg_list ',' tSTAR
+                | f_arg ',' tSTAR
                     {
+                      local_add_f(p, 0);
                       $$ = list3($1, (node*)-1, 0);
                     }
-                | f_marg_list ',' tSTAR ',' f_marg_list
+                | f_arg ',' tSTAR ',' f_arg
                     {
                       $$ = list3($1, (node*)-1, $5);
                     }
@@ -2437,96 +2593,134 @@ f_margs         : f_marg_list
                     {
                       $$ = list3(0, new_arg(p, $2), 0);
                     }
-                | tSTAR f_norm_arg ',' f_marg_list
+                | tSTAR f_norm_arg ',' f_arg
                     {
                       $$ = list3(0, new_arg(p, $2), $4);
                     }
                 | tSTAR
                     {
+                      local_add_f(p, 0);
                       $$ = list3(0, (node*)-1, 0);
                     }
-                | tSTAR ',' f_marg_list
+                | tSTAR ','
+                    {
+                      local_add_f(p, 0);
+                    }
+                  f_arg
+                    {
+                      $$ = list3(0, (node*)-1, $4);
+                    }
+                ;
+
+block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg
+                    {
+                      $$ = new_args_tail(p, $1, $3, $4);
+                    }
+                | f_block_kwarg opt_f_block_arg
+                    {
+                      $$ = new_args_tail(p, $1, 0, $2);
+                    }
+                | f_kwrest opt_f_block_arg
+                    {
+                      $$ = new_args_tail(p, 0, $1, $2);
+                    }
+                | f_block_arg
+                    {
+                      $$ = new_args_tail(p, 0, 0, $1);
+                    }
+                ;
+
+opt_block_args_tail : ',' block_args_tail
+                    {
+                      $$ = $2;
+                    }
+                | /* none */
                     {
-                      $$ = list3(0, (node*)-1, $3);
+                      $$ = new_args_tail(p, 0, 0, 0);
                     }
                 ;
 
-block_param     : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
+block_param     : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail
                     {
                       $$ = new_args(p, $1, $3, $5, 0, $6);
                     }
-                | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+                | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
                     {
                       $$ = new_args(p, $1, $3, $5, $7, $8);
                     }
-                | f_arg ',' f_block_optarg opt_f_block_arg
+                | f_arg ',' f_block_optarg opt_block_args_tail
                     {
                       $$ = new_args(p, $1, $3, 0, 0, $4);
                     }
-                | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg
+                | f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail
                     {
                       $$ = new_args(p, $1, $3, 0, $5, $6);
                     }
-                | f_arg ',' f_rest_arg opt_f_block_arg
+                | f_arg ',' f_rest_arg opt_block_args_tail
                     {
                       $$ = new_args(p, $1, 0, $3, 0, $4);
                     }
-                | f_arg ','
+                | f_arg ',' opt_block_args_tail
                     {
-                      $$ = new_args(p, $1, 0, 0, 0, 0);
+                      $$ = new_args(p, $1, 0, 0, 0, $3);
                     }
-                | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+                | f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail
                     {
                       $$ = new_args(p, $1, 0, $3, $5, $6);
                     }
-                | f_arg opt_f_block_arg
+                | f_arg opt_block_args_tail
                     {
                       $$ = new_args(p, $1, 0, 0, 0, $2);
                     }
-                | f_block_optarg ',' f_rest_arg opt_f_block_arg
+                | f_block_optarg ',' f_rest_arg opt_block_args_tail
                     {
                       $$ = new_args(p, 0, $1, $3, 0, $4);
                     }
-                | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+                | f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
                     {
                       $$ = new_args(p, 0, $1, $3, $5, $6);
                     }
-                | f_block_optarg opt_f_block_arg
+                | f_block_optarg opt_block_args_tail
                     {
                       $$ = new_args(p, 0, $1, 0, 0, $2);
                     }
-                | f_block_optarg ',' f_arg opt_f_block_arg
+                | f_block_optarg ',' f_arg opt_block_args_tail
                     {
                       $$ = new_args(p, 0, $1, 0, $3, $4);
                     }
-                | f_rest_arg opt_f_block_arg
+                | f_rest_arg opt_block_args_tail
                     {
                       $$ = new_args(p, 0, 0, $1, 0, $2);
                     }
-                | f_rest_arg ',' f_arg opt_f_block_arg
+                | f_rest_arg ',' f_arg opt_block_args_tail
                     {
                       $$ = new_args(p, 0, 0, $1, $3, $4);
                     }
-                | f_block_arg
+                | block_args_tail
                     {
                       $$ = new_args(p, 0, 0, 0, 0, $1);
                     }
                 ;
 
 opt_block_param : none
-                | block_param_def
                     {
+                      local_add_blk(p, 0);
+                      $$ = 0;
+                    }
+                | block_param_def
+                   {
                       p->cmd_start = TRUE;
                       $$ = $1;
                     }
                 ;
 
-block_param_def : '|' opt_bv_decl '|'
+block_param_def : '|' {local_add_blk(p, 0);} opt_bv_decl '|'
                     {
                       $$ = 0;
                     }
                 | tOROP
                     {
+                      local_add_blk(p, 0);
                       $$ = 0;
                     }
                 | '|' block_param opt_bv_decl '|'
@@ -2739,7 +2933,14 @@ literal         : numeric
                 | symbols
                 ;
 
-string          : tCHAR
+string          : string_fragment
+                | string string_fragment
+                    {
+                      $$ = concat_string(p, $1, $2);
+                    }
+                ;
+
+string_fragment : tCHAR
                 | tSTRING
                 | tSTRING_BEG tSTRING
                     {
@@ -2961,7 +3162,7 @@ var_ref         : variable
                     }
                 | keyword__FILE__
                     {
-                      const char *fn = p->filename;
+                      const char *fn = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
                       if (!fn) {
                         fn = "(null)";
                       }
@@ -3021,65 +3222,151 @@ f_arglist       : '(' f_args rparen
                     }
                 ;
 
-f_args          : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg
+f_label         : tIDENTIFIER tLABEL_TAG
+                ;
+
+f_kw            : f_label arg
+                    {
+                      void_expr_error(p, $2);
+                      $$ = new_kw_arg(p, $1, $2);
+                    }
+                | f_label
+                    {
+                      $$ = new_kw_arg(p, $1, 0);
+                    }
+                ;
+
+f_block_kw      : f_label primary_value
+                    {
+                      $$ = new_kw_arg(p, $1, $2);
+                    }
+                | f_label
+                    {
+                      $$ = new_kw_arg(p, $1, 0);
+                    }
+                ;
+
+f_block_kwarg   : f_block_kw
+                    {
+                      $$ = list1($1);
+                    }
+                | f_block_kwarg ',' f_block_kw
+                    {
+                      $$ = push($1, $3);
+                    }
+                ;
+
+f_kwarg         : f_kw
+                    {
+                      $$ = list1($1);
+                    }
+                | f_kwarg ',' f_kw
+                    {
+                      $$ = push($1, $3);
+                    }
+                ;
+
+kwrest_mark     : tPOW
+                | tDSTAR
+                ;
+
+f_kwrest        : kwrest_mark tIDENTIFIER
+                    {
+                      $$ = cons((node*)NODE_KW_REST_ARGS, nsym($2));
+                    }
+                | kwrest_mark
+                    {
+                      $$ = cons((node*)NODE_KW_REST_ARGS, 0);
+                    }
+                ;
+
+args_tail       : f_kwarg ',' f_kwrest opt_f_block_arg
+                    {
+                      $$ = new_args_tail(p, $1, $3, $4);
+                    }
+                | f_kwarg opt_f_block_arg
+                    {
+                      $$ = new_args_tail(p, $1, 0, $2);
+                    }
+                | f_kwrest opt_f_block_arg
+                    {
+                      $$ = new_args_tail(p, 0, $1, $2);
+                    }
+                | f_block_arg
+                    {
+                      $$ = new_args_tail(p, 0, 0, $1);
+                    }
+                ;
+
+opt_args_tail   : ',' args_tail
+                    {
+                      $$ = $2;
+                    }
+                | /* none */
+                    {
+                      $$ = new_args_tail(p, 0, 0, 0);
+                    }
+                ;
+
+f_args          : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
                     {
                       $$ = new_args(p, $1, $3, $5, 0, $6);
                     }
-                | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+                | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
                     {
                       $$ = new_args(p, $1, $3, $5, $7, $8);
                     }
-                | f_arg ',' f_optarg opt_f_block_arg
+                | f_arg ',' f_optarg opt_args_tail
                     {
                       $$ = new_args(p, $1, $3, 0, 0, $4);
                     }
-                | f_arg ',' f_optarg ',' f_arg opt_f_block_arg
+                | f_arg ',' f_optarg ',' f_arg opt_args_tail
                     {
                       $$ = new_args(p, $1, $3, 0, $5, $6);
                     }
-                | f_arg ',' f_rest_arg opt_f_block_arg
+                | f_arg ',' f_rest_arg opt_args_tail
                     {
                       $$ = new_args(p, $1, 0, $3, 0, $4);
                     }
-                | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+                | f_arg ',' f_rest_arg ',' f_arg opt_args_tail
                     {
                       $$ = new_args(p, $1, 0, $3, $5, $6);
                     }
-                | f_arg opt_f_block_arg
+                | f_arg opt_args_tail
                     {
                       $$ = new_args(p, $1, 0, 0, 0, $2);
                     }
-                | f_optarg ',' f_rest_arg opt_f_block_arg
+                | f_optarg ',' f_rest_arg opt_args_tail
                     {
                       $$ = new_args(p, 0, $1, $3, 0, $4);
                     }
-                | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+                | f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
                     {
                       $$ = new_args(p, 0, $1, $3, $5, $6);
                     }
-                | f_optarg opt_f_block_arg
+                | f_optarg opt_args_tail
                     {
                       $$ = new_args(p, 0, $1, 0, 0, $2);
                     }
-                | f_optarg ',' f_arg opt_f_block_arg
+                | f_optarg ',' f_arg opt_args_tail
                     {
                       $$ = new_args(p, 0, $1, 0, $3, $4);
                     }
-                | f_rest_arg opt_f_block_arg
+                | f_rest_arg opt_args_tail
                     {
                       $$ = new_args(p, 0, 0, $1, 0, $2);
                     }
-                | f_rest_arg ',' f_arg opt_f_block_arg
+                | f_rest_arg ',' f_arg opt_args_tail
                     {
                       $$ = new_args(p, 0, 0, $1, $3, $4);
                     }
-                | f_block_arg
+                | args_tail
                     {
                       $$ = new_args(p, 0, 0, 0, 0, $1);
                     }
                 | /* none */
                     {
-                      local_add_f(p, 0);
+                      local_add_f(p, mrb_intern_lit(p->mrb, "&"));
                       $$ = new_args(p, 0, 0, 0, 0, 0);
                     }
                 ;
@@ -3121,9 +3408,15 @@ f_arg_item      : f_norm_arg
                     {
                       $$ = new_arg(p, $1);
                     }
-                | tLPAREN f_margs rparen
+                | tLPAREN
                     {
-                      $$ = new_masgn(p, $2, 0);
+                      $<nd>$ = local_switch(p);
+                    }
+                  f_margs rparen
+                    {
+                      $$ = new_masgn_param(p, $3, p->locals->car);
+                      local_resume(p, $<nd>2);
+                      local_add_f(p, 0);
                     }
                 ;
 
@@ -3189,7 +3482,7 @@ f_rest_arg      : restarg_mark tIDENTIFIER
                     }
                 | restarg_mark
                     {
-                      local_add_f(p, 0);
+                      local_add_f(p, mrb_intern_lit(p->mrb, "*"));
                       $$ = -1;
                     }
                 ;
@@ -3200,7 +3493,6 @@ blkarg_mark     : '&'
 
 f_block_arg     : blkarg_mark tIDENTIFIER
                     {
-                      local_add_f(p, $2);
                       $$ = $2;
                     }
                 ;
@@ -3211,7 +3503,6 @@ opt_f_block_arg : ',' f_block_arg
                     }
                 | none
                     {
-                      local_add_f(p, 0);
                       $$ = 0;
                     }
                 ;
@@ -3275,7 +3566,7 @@ assoc           : arg tASSOC arg
                       void_expr_error(p, $3);
                       $$ = cons(new_sym(p, $1), $3);
                     }
-                | string tLABEL_TAG arg
+                | string_fragment tLABEL_TAG arg
                     {
                       void_expr_error(p, $3);
                       if ($1->car == (node*)NODE_DSTR) {
@@ -3285,6 +3576,11 @@ assoc           : arg tASSOC arg
                         $$ = cons(new_sym(p, new_strsym(p, $1)), $3);
                       }
                     }
+                | tDSTAR arg
+                    {
+                      void_expr_error(p, $2);
+                      $$ = cons(cons((node*)NODE_KW_REST_ARGS, 0), $2);
+                    }
                 ;
 
 operation       : tIDENTIFIER
@@ -3375,8 +3671,9 @@ yyerror(parser_state *p, const char *s)
 
   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);
+    if (p->filename_sym) {
+      const char *filename = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
+      fprintf(stderr, "%s:%d:%d: %s\n", filename, p->lineno, p->column, s);
     }
     else {
       fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
@@ -3411,11 +3708,12 @@ yywarn(parser_state *p, const char *s)
 
   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);
+    if (p->filename_sym) {
+      const char *filename = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
+      fprintf(stderr, "%s:%d:%d: warning: %s\n", filename, p->lineno, p->column, s);
     }
     else {
-      fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
+      fprintf(stderr, "line %d:%d: warning: %s\n", p->lineno, p->column, s);
     }
 #endif
   }
@@ -3450,13 +3748,13 @@ backref_error(parser_state *p, node *n)
 {
   int c;
 
-  c = (int)(intptr_t)n->car;
+  c = intn(n->car);
 
   if (c == NODE_NTH_REF) {
-    yyerror_i(p, "can't set variable $%" MRB_PRId, (int)(intptr_t)n->cdr);
+    yyerror_i(p, "can't set variable $%" MRB_PRId, intn(n->cdr));
   }
   else if (c == NODE_BACK_REF) {
-    yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
+    yyerror_i(p, "can't set variable $%c", intn(n->cdr));
   }
   else {
     mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
@@ -3469,7 +3767,7 @@ void_expr_error(parser_state *p, node *n)
   int c;
 
   if (n == NULL) return;
-  c = (int)(intptr_t)n->car;
+  c = intn(n->car);
   switch (c) {
   case NODE_BREAK:
   case NODE_RETURN:
@@ -3480,8 +3778,10 @@ void_expr_error(parser_state *p, node *n)
     break;
   case NODE_AND:
   case NODE_OR:
-    void_expr_error(p, n->cdr->car);
-    void_expr_error(p, n->cdr->cdr);
+    if (n->cdr) {
+      void_expr_error(p, n->cdr->car);
+      void_expr_error(p, n->cdr->cdr);
+    }
     break;
   case NODE_BEGIN:
     if (n->cdr) {
@@ -3508,7 +3808,7 @@ nextc(parser_state *p)
   if (p->pb) {
     node *tmp;
 
-    c = (int)(intptr_t)p->pb->car;
+    c = intn(p->pb->car);
     tmp = p->pb;
     p->pb = p->pb->cdr;
     cons_free(tmp);
@@ -3532,14 +3832,6 @@ nextc(parser_state *p)
   if (c >= 0) {
     p->column++;
   }
-  if (c == '\r') {
-    c = nextc(p);
-    if (c != '\n') {
-      pushback(p, c);
-      return '\r';
-    }
-    return c;
-  }
   return c;
 
   eof:
@@ -3557,7 +3849,7 @@ pushback(parser_state *p, int c)
   if (c >= 0) {
     p->column--;
   }
-  p->pb = cons((node*)(intptr_t)c, p->pb);
+  p->pb = cons(nint(c), p->pb);
 }
 
 static void
@@ -3582,7 +3874,7 @@ peekc_n(parser_state *p, int n)
     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);
+    list = push(list, nint(c0));
   } while(n--);
   if (p->pb) {
     p->pb = append((node*)list, p->pb);
@@ -4019,11 +4311,11 @@ parse_string(parser_state *p)
     }
     else if (c == beg) {
       nest_level++;
-      p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+      p->lex_strterm->cdr->car = nint(nest_level);
     }
     else if (c == end) {
       nest_level--;
-      p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+      p->lex_strterm->cdr->car = nint(nest_level);
     }
     else if (c == '\\') {
       c = nextc(p);
@@ -4365,7 +4657,16 @@ parser_yylex(parser_state *p)
         return tOP_ASGN;
       }
       pushback(p, c);
-      c = tPOW;
+      if (IS_SPCARG(c)) {
+        yywarning(p, "'**' interpreted as argument prefix");
+        c = tDSTAR;
+      }
+      else if (IS_BEG()) {
+        c = tDSTAR;
+      }
+      else {
+        c = tPOW; /* "**", "argument prefix" */
+      }
     }
     else {
       if (c == '=') {
@@ -4589,10 +4890,10 @@ parser_yylex(parser_state *p)
     }
     newtok(p);
     /* need support UTF-8 if configured */
-    if ((isalnum(c) || c == '_')) {
+    if ((ISALNUM(c) || c == '_')) {
       int c2 = nextc(p);
       pushback(p, c2);
-      if ((isalnum(c2) || c2 == '_')) {
+      if ((ISALNUM(c2) || c2 == '_')) {
         goto ternary;
       }
     }
@@ -4956,7 +5257,7 @@ parser_yylex(parser_state *p)
     tokfix(p);
     if (is_float) {
 #ifdef MRB_WITHOUT_FLOAT
-      yywarning_s(p, "floating point numbers are not supported", tok(p));
+      yywarning(p, "floating point numbers are not supported");
       pylval.nd = new_int(p, "0", 10);
       return tINTEGER;
 #else
@@ -5160,7 +5461,7 @@ parser_yylex(parser_state *p)
       }
       else {
         term = nextc(p);
-        if (isalnum(term)) {
+        if (ISALNUM(term)) {
           yyerror(p, "unknown type of %string");
           return 0;
         }
@@ -5304,7 +5605,7 @@ parser_yylex(parser_state *p)
       do {
         tokadd(p, c);
         c = nextc(p);
-      } while (c >= 0 && isdigit(c));
+      } while (c >= 0 && ISDIGIT(c));
       pushback(p, c);
       if (last_state == EXPR_FNAME) goto gvar;
       tokfix(p);
@@ -5346,7 +5647,7 @@ parser_yylex(parser_state *p)
         }
         return 0;
       }
-      else if (isdigit(c)) {
+      else if (ISDIGIT(c)) {
         if (p->tidx == 1) {
           yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
         }
@@ -5503,7 +5804,7 @@ parser_yylex(parser_state *p)
       mrb_sym ident = intern_cstr(tok(p));
 
       pylval.id = ident;
-      if (last_state != EXPR_DOT && islower(tok(p)[0]) && local_var_p(p, ident)) {
+      if (last_state != EXPR_DOT && ISLOWER(tok(p)[0]) && local_var_p(p, ident)) {
         p->lstate = EXPR_END;
       }
     }
@@ -5534,6 +5835,7 @@ parser_init_cxt(parser_state *p, mrbc_context *cxt)
   }
   p->capture_errors = cxt->capture_errors;
   p->no_optimize = cxt->no_optimize;
+  p->on_eval = cxt->on_eval;
   if (cxt->partial_hook) {
     p->cxt = cxt;
   }
@@ -5546,7 +5848,7 @@ parser_update_cxt(parser_state *p, mrbc_context *cxt)
   int i = 0;
 
   if (!cxt) return;
-  if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return;
+  if (intn(p->tree->car) != NODE_SCOPE) return;
   n0 = n = p->tree->cdr->car;
   while (n) {
     i++;
@@ -5712,7 +6014,7 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
   mrb_sym* new_table;
 
   sym = mrb_intern_cstr(p->mrb, f);
-  p->filename = mrb_sym2name_len(p->mrb, sym, NULL);
+  p->filename_sym = sym;
   p->lineno = (p->filename_table_length > 0)? 0 : 1;
 
   for (i = 0; i < p->filename_table_length; ++i) {
@@ -5722,7 +6024,11 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
     }
   }
 
-  p->current_filename_index = (int)p->filename_table_length++;
+  if (p->filename_table_length == UINT16_MAX) {
+    yyerror(p, "too many files to compile");
+    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) {
@@ -5732,11 +6038,11 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
   p->filename_table[p->filename_table_length - 1] = sym;
 }
 
-MRB_API char const*
+MRB_API mrb_sym
 mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
-  if (idx >= p->filename_table_length) { return NULL; }
+  if (idx >= p->filename_table_length) return 0;
   else {
-    return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL);
+    return p->filename_table[idx];
   }
 }
 
@@ -5896,6 +6202,48 @@ dump_recur(mrb_state *mrb, node *tree, int offset)
   }
 }
 
+static void
+dump_args(mrb_state *mrb, node *n, int offset)
+{
+  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=\n", mrb_sym2name(mrb, sym(n2->car->car)));
+        mrb_parser_dump(mrb, n2->car->cdr, offset+3);
+        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) {
+    mrb_assert(intn(n->car) == NODE_ARGS_TAIL);
+    mrb_parser_dump(mrb, n, offset);
+  }
+}
+
 #endif
 
 void
@@ -5907,7 +6255,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
   if (!tree) return;
   again:
   dump_prefix(tree, offset);
-  nodetype = (int)(intptr_t)tree->car;
+  nodetype = intn(tree->car);
   tree = tree->cdr;
   switch (nodetype) {
   case NODE_BEGIN:
@@ -5967,7 +6315,8 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     break;
 
   case NODE_LAMBDA:
-    printf("NODE_BLOCK:\n");
+    printf("NODE_LAMBDA:\n");
+    dump_prefix(tree, offset);
     goto block;
 
   case NODE_BLOCK:
@@ -5975,43 +6324,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     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_args(mrb, tree->car, offset+1);
     }
     dump_prefix(tree, offset+1);
     printf("body:\n");
@@ -6163,7 +6476,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     dump_prefix(tree, offset+1);
     printf("method='%s' (%d)\n",
         mrb_sym2name(mrb, sym(tree->cdr->car)),
-        (int)(intptr_t)tree->cdr->car);
+        intn(tree->cdr->car));
     tree = tree->cdr->cdr->car;
     if (tree) {
       dump_prefix(tree, offset+1);
@@ -6218,6 +6531,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     }
     break;
 
+  case NODE_KW_HASH:
+    printf("NODE_KW_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);
@@ -6280,7 +6606,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     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);
+    printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), intn(tree->car));
     tree = tree->cdr;
     mrb_parser_dump(mrb, tree->car, offset+1);
     break;
@@ -6362,11 +6688,11 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     break;
 
   case NODE_BACK_REF:
-    printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree);
+    printf("NODE_BACK_REF: $%c\n", intn(tree));
     break;
 
   case NODE_NTH_REF:
-    printf("NODE_NTH_REF: $%" MRB_PRId "\n", (mrb_int)(intptr_t)tree);
+    printf("NODE_NTH_REF: $%d\n", intn(tree));
     break;
 
   case NODE_ARG:
@@ -6379,7 +6705,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     break;
 
   case NODE_INT:
-    printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car);
+    printf("NODE_INT %s base %d\n", (char*)tree->car, intn(tree->cdr->car));
     break;
 
   case NODE_FLOAT:
@@ -6392,7 +6718,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     break;
 
   case NODE_STR:
-    printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+    printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, intn(tree->cdr));
     break;
 
   case NODE_DSTR:
@@ -6401,7 +6727,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     break;
 
   case NODE_XSTR:
-    printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+    printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, intn(tree->cdr));
     break;
 
   case NODE_DXSTR:
@@ -6430,7 +6756,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
 
   case NODE_SYM:
     printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)),
-           (int)(intptr_t)tree);
+           intn(tree));
     break;
 
   case NODE_SELF:
@@ -6546,43 +6872,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     }
     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_args(mrb, tree->car, offset);
     }
     mrb_parser_dump(mrb, tree->cdr->car, offset+1);
     break;
@@ -6595,44 +6885,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     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)));
-      }
+      dump_args(mrb, tree->car, offset+1);
     }
     tree = tree->cdr;
     mrb_parser_dump(mrb, tree->car, offset+1);
@@ -6648,6 +6901,37 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
     break;
 
+  case NODE_ARGS_TAIL:
+    printf("NODE_ARGS_TAIL:\n");
+    {
+      node *kws = tree->car;
+
+      while (kws) {
+        mrb_parser_dump(mrb, kws->car, offset+1);
+        kws = kws->cdr;
+      }
+    }
+    tree = tree->cdr;
+    if (tree->car) {
+      mrb_assert(intn(tree->car->car) == NODE_KW_REST_ARGS);
+      mrb_parser_dump(mrb, tree->car, offset+1);
+    }
+    tree = tree->cdr;
+    if (tree->car) {
+      dump_prefix(tree, offset+1);
+      printf("block='%s'\n", mrb_sym2name(mrb, sym(tree->car)));
+    }
+    break;
+
+  case NODE_KW_ARG:
+    printf("NODE_KW_ARG %s\n", mrb_sym2name(mrb, sym(tree->car)));
+    mrb_parser_dump(mrb, tree->cdr->car, offset + 1);
+    break;
+
+  case NODE_KW_REST_ARGS:
+    printf("NODE_KW_REST_ARGS %s\n", mrb_sym2name(mrb, sym(tree)));
+    break;
+
   default:
     printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
     break;
index 3bf6d6a..fa191e6 100644 (file)
@@ -23,10 +23,10 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
       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|
+  file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y", lex_def] do |t|
+    FileUtils.mkdir_p File.dirname t.name
     yacc.run t.name, t.prerequisites.first
   end
 
@@ -35,6 +35,6 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
     gperf.run t.name, t.prerequisites.first
   end
 
-  file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs
+  file build.libmruby_core_static => core_objs
   build.libmruby << core_objs
 end
diff --git a/third-party/mruby/mrbgems/mruby-enum-chain/mrbgem.rake b/third-party/mruby/mrbgems/mruby-enum-chain/mrbgem.rake
new file mode 100644 (file)
index 0000000..7294f26
--- /dev/null
@@ -0,0 +1,6 @@
+MRuby::Gem::Specification.new('mruby-enum-chain') do |spec|
+  spec.license = 'MIT'
+  spec.author  = 'mruby developers'
+  spec.summary = 'Enumerator::Chain class'
+  spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
+end
diff --git a/third-party/mruby/mrbgems/mruby-enum-chain/mrblib/chain.rb b/third-party/mruby/mrbgems/mruby-enum-chain/mrblib/chain.rb
new file mode 100644 (file)
index 0000000..98515ea
--- /dev/null
@@ -0,0 +1,60 @@
+##
+# chain.rb Enumerator::Chain class
+# See Copyright Notice in mruby.h
+
+module Enumerable
+  def chain(*args)
+    Enumerator::Chain.new(self, *args)
+  end
+
+  def +(other)
+    Enumerator::Chain.new(self, other)
+  end
+end
+
+class Enumerator
+  class Chain
+    include Enumerable
+
+    def initialize(*args)
+      @enums = args
+    end
+
+    def initialize_copy(orig)
+      @enums = orig.__copy_enums
+    end
+
+    def each(&block)
+      return to_enum unless block_given?
+
+      @enums.each { |e| e.each(&block) }
+
+      self
+    end
+
+    def size
+      @enums.reduce(0) do |a, e|
+        return nil unless e.respond_to?(:size)
+        a + e.size
+      end
+    end
+
+    def rewind
+      @enums.reverse_each do |e|
+        e.rewind if e.respond_to?(:rewind)
+      end
+
+      self
+    end
+
+    def inspect
+      "#<#{self.class}: #{@enums.inspect}>"
+    end
+
+    def __copy_enums
+      @enums.each_with_object([]) do |e, a|
+        a << e.clone
+      end
+    end
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-enum-chain/test/enum_chain.rb b/third-party/mruby/mrbgems/mruby-enum-chain/test/enum_chain.rb
new file mode 100644 (file)
index 0000000..4dd59bd
--- /dev/null
@@ -0,0 +1,76 @@
+##
+# Enumerator::Chain test
+
+assert("Enumerable#chain") do
+  a = []
+  b = {}
+  c = Object.new # not has #each method
+
+  assert_kind_of Enumerator::Chain, a.chain
+  assert_kind_of Enumerator::Chain, a.chain(b)
+  assert_kind_of Enumerator::Chain, a.chain(b, c)
+  assert_raise(NoMethodError) { c.chain }
+end
+
+assert("Enumerable#+") do
+  a = [].each
+  b = {}.reverse_each
+  c = Object.new # not has #each method
+
+  assert_kind_of Enumerator::Chain, a + b
+  assert_kind_of Enumerator::Chain, a + c
+  assert_kind_of Enumerator::Chain, b + a
+  assert_kind_of Enumerator::Chain, b + c
+  assert_raise(NoMethodError) { c + a }
+end
+
+assert("Enumerator.new") do
+  a = []
+  b = {}
+  c = Object.new # not has #each method
+
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, a)
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, b)
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, c)
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, a)
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, b)
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, c)
+  assert_kind_of Enumerator::Chain, Enumerator::Chain.new(c, a)
+end
+
+assert("Enumerator::Chain#each") do
+  a = [1, 2, 3]
+
+  aa = a.chain(a)
+  assert_kind_of Enumerator, aa.each
+  assert_equal [1, 2, 3, 1, 2, 3], aa.each.to_a
+
+  aa = a.chain(a.reverse_each)
+  assert_kind_of Enumerator, aa.each
+  assert_equal [1, 2, 3, 3, 2, 1], aa.each.to_a
+
+  aa = a.chain(a.reverse_each, a)
+  assert_kind_of Enumerator, aa.each
+  assert_equal [1, 2, 3, 3, 2, 1, 1, 2, 3], aa.each.to_a
+
+  aa = a.chain(Object.new)
+  assert_kind_of Enumerator, aa.each
+  assert_raise(NoMethodError) {  aa.each.to_a }
+end
+
+assert("Enumerator::Chain#size") do
+  a = [1, 2, 3]
+
+  aa = a.chain(a)
+  assert_equal 6, aa.size
+
+  aa = a.chain(a.reverse_each)
+  assert_nil aa.size
+
+  aa = a.chain(a.reverse_each, a)
+  assert_nil aa.size
+
+  aa = a.chain(Object.new)
+  assert_nil aa.size
+end
index a840ade..99b9cdd 100644 (file)
@@ -13,10 +13,9 @@ module Enumerable
   #    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)
+    n = n.__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
@@ -57,8 +56,8 @@ module Enumerable
   #    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
+    n = n.__to_int
+    i = n.to_i
     raise ArgumentError, "attempt to take negative size" if i < 0
     ary = []
     return ary if i == 0
@@ -113,12 +112,12 @@ module Enumerable
   #     [8, 9, 10]
 
   def each_cons(n, &block)
-    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+    n = n.__to_int
     raise ArgumentError, "invalid size" if n <= 0
 
     return to_enum(:each_cons,n) unless block
     ary = []
-    n = n.to_int
+    n = n.to_i
     self.each do |*val|
       ary.shift if ary.size == n
       ary << val.__svalue
@@ -141,12 +140,12 @@ module Enumerable
   #     [10]
 
   def each_slice(n, &block)
-    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+    n = n.__to_int
     raise ArgumentError, "invalid slice size" if n <= 0
 
     return to_enum(:each_slice,n) unless block
     ary = []
-    n = n.to_int
+    n = n.to_i
     self.each do |*val|
       ary << val.__svalue
       if ary.size == n
@@ -201,14 +200,11 @@ module Enumerable
       ary.push([block.call(e), i])
     }
     if ary.size > 1
-      ary.__sort_sub__(0, ary.size - 1) do |a,b|
-        a <=> b
-      end
+      ary.sort!
     end
     ary.collect{|e,i| orig[i]}
   end
 
-  NONE = Object.new
   ##
   # call-seq:
   #    enum.first       ->  obj or nil
@@ -225,9 +221,7 @@ module Enumerable
       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
+      i = args[0].__to_int
       raise ArgumentError, "attempt to take negative size" if i < 0
       ary = []
       return ary if i == 0
@@ -675,13 +669,7 @@ module Enumerable
     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
+      n = nv.__to_int
       return nil if n <= 0
     end
 
@@ -803,13 +791,22 @@ module Enumerable
   #       # => {:hello => 0, :world => 1}
   #
 
-  def to_h
+  def to_h(&blk)
     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]
+    if blk
+      self.each do |v|
+        v = blk.call(v)
+        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
+    else
+      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
     end
     h
   end
index 46ed5f0..64b1bbd 100644 (file)
@@ -128,14 +128,14 @@ assert("Enumerable#any? (enhancement)") do
 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_equal [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_same r, r.reverse_each { |v| a << v }
   assert_equal [3, 2, 1], a
 end
 
@@ -188,4 +188,6 @@ assert("Enumerable#to_h") do
   assert_equal h0, h
   # mruby-enum-ext also provides nil.to_h
   assert_equal Hash.new, nil.to_h
+
+  assert_equal({1=>4,3=>8}, c.new.to_h{|k,v|[k,v*2]})
 end
index 9227abe..e4f116a 100644 (file)
@@ -44,7 +44,7 @@ class Enumerator
 
     def to_enum(meth=:each, *args, &block)
       unless self.respond_to?(meth)
-        raise NoMethodError, "undefined method #{meth}"
+        raise ArgumentError, "undefined method #{meth}"
       end
       lz = Lazy.new(self, &block)
       lz.obj = self
index 7ca1d5e..4576872 100644 (file)
@@ -109,27 +109,30 @@ class Enumerator
   #
   #     p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
   #
-  def initialize(obj=nil, meth=:each, *args, &block)
+  # In the second, deprecated, form, a generated Enumerator iterates over the
+  # given object using the given method with the given arguments passed. This
+  # form is left only for internal use.
+  #
+  # Use of this form is discouraged.  Use Kernel#enum_for or Kernel#to_enum
+  # instead.
+  def initialize(obj=NONE, meth=:each, *args, &block)
     if block
       obj = Generator.new(&block)
-    else
-      raise ArgumentError unless obj
-    end
-    if @obj and !self.respond_to?(meth)
-      raise NoMethodError, "undefined method #{meth}"
+    elsif obj == NONE
+      raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
     end
 
     @obj = obj
     @meth = meth
-    @args = args.dup
+    @args = args
     @fib = nil
     @dst = nil
     @lookahead = nil
     @feedvalue = nil
     @stop_exc = false
   end
-  attr_accessor :obj, :meth, :args, :fib
-  private :obj, :meth, :args, :fib
+  attr_accessor :obj, :meth, :args
+  attr_reader :fib
 
   def initialize_copy(obj)
     raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
@@ -157,12 +160,10 @@ class Enumerator
   def with_index(offset=0, &block)
     return to_enum :with_index, offset unless block
 
-    offset = if offset.nil?
-      0
-    elsif offset.respond_to?(:to_int)
-      offset.to_int
+    if offset.nil?
+      offset = 0
     else
-      raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+      offset = offset.__to_int
     end
 
     n = offset - 1
@@ -223,13 +224,11 @@ class Enumerator
   end
 
   def inspect
-    return "#<#{self.class}: uninitialized>" unless @obj
-
     if @args && @args.size > 0
       args = @args.join(", ")
-      "#<#{self.class}: #{@obj}:#{@meth}(#{args})>"
+      "#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>"
     else
-      "#<#{self.class}: #{@obj}:#{@meth}>"
+      "#<#{self.class}: #{@obj.inspect}:#{@meth}>"
     end
   end
 
@@ -623,9 +622,7 @@ module Enumerable
   # use Enumerator to use infinite sequence
   def zip(*args, &block)
     args = args.map do |a|
-      if a.respond_to?(:to_ary)
-        a.to_ary.to_enum(:each)
-      elsif a.respond_to?(:each)
+      if a.respond_to?(:each)
         a.to_enum(:each)
       else
         raise TypeError, "wrong argument type #{a.class} (must respond to :each)"
index 428ea03..d609cad 100644 (file)
@@ -6,11 +6,11 @@ class << @obj
   end
 end
 
-assert 'Enumerator' do
+assert 'Enumerator.class' do
   assert_equal Class, Enumerator.class
 end
 
-assert 'Enumerator' do
+assert 'Enumerator.superclass' do
   assert_equal Object, Enumerator.superclass
 end
 
@@ -19,11 +19,8 @@ assert 'Enumerator.new' do
   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|
@@ -55,12 +52,6 @@ assert 'Enumerator#with_index' do
   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
@@ -99,11 +90,13 @@ 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)
+  assert_equal('#<Enumerator: 0..10:each>', e.inspect)
+  e = 'FooObject'.enum_for(:foo, 1)
+  assert_equal('#<Enumerator: "FooObject":foo(1)>', e.inspect)
+  e = 'FooObject'.enum_for(:foo, 1, 2, 3)
+  assert_equal('#<Enumerator: "FooObject":foo(1, 2, 3)>', e.inspect)
+  e = nil.enum_for(:to_s)
+  assert_equal('#<Enumerator: nil:to_s>', e.inspect)
 end
 
 assert 'Enumerator#each' do
@@ -425,8 +418,10 @@ assert 'nested iteration' do
 end
 
 assert 'Kernel#to_enum' do
+  e = nil
   assert_equal Enumerator, [].to_enum.class
-  assert_raise(ArgumentError){ nil.to_enum }
+  assert_nothing_raised { e = [].to_enum(:_not_implemented_) }
+  assert_raise(NoMethodError) { e.first }
 end
 
 assert 'modifying existing methods' do
index 14e89ac..fa687d6 100644 (file)
@@ -44,7 +44,7 @@ search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
   return NULL;
 }
 
-static inline mrb_code
+static uint16_t
 search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
 {
   mrb_irep *virep;
@@ -57,7 +57,7 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
     }
     for (pos = 0; pos < virep->nlocals - 1; pos++) {
       if (vsym == virep->lv[pos].name) {
-        return (MKARG_B(pos + 1) | MKARG_C(level + bnest));
+        return (pos+1)<<8 | (level+bnest);
       }
     }
   }
@@ -71,8 +71,8 @@ irep_argc(mrb_irep *irep)
   mrb_code c;
 
   c = irep->iseq[0];
-  if (GET_OPCODE(c) == OP_ENTER) {
-    mrb_aspec ax = GETARG_Ax(c);
+  if (c == OP_ENTER) {
+    mrb_aspec ax = PEEK_W(irep->iseq+1);
     /* 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;
   }
@@ -88,95 +88,132 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
   return TRUE;
 }
 
+extern uint8_t mrb_insn_size[];
+extern uint8_t mrb_insn_size1[];
+extern uint8_t mrb_insn_size2[];
+extern uint8_t mrb_insn_size3[];
+
 static void
 patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
 {
   int i;
-  mrb_code c;
+  uint32_t a;
+  uint16_t b;
+  uint8_t c;
+  mrb_code insn;
   int argc = irep_argc(irep);
 
-  for (i = 0; i < irep->ilen; i++) {
-    c = irep->iseq[i];
-    switch(GET_OPCODE(c)){
+  for (i = 0; i < irep->ilen; ) {
+    insn = irep->iseq[i];
+    switch(insn){
     case OP_EPUSH:
-      patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top);
+      b = PEEK_S(irep->iseq+i+1);
+      patch_irep(mrb, irep->reps[b], 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);
-        }
-      }
+    case OP_BLOCK:
+      a = PEEK_B(irep->iseq+i+1);
+      b = PEEK_B(irep->iseq+i+2);
+      patch_irep(mrb, irep->reps[b], bnest + 1, top);
       break;
 
     case OP_SEND:
-      if (GETARG_C(c) != 0) {
+      b = PEEK_B(irep->iseq+i+2);
+      c = PEEK_B(irep->iseq+i+3);
+      if (c != 0) {
         break;
       }
       else {
-        mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest);
+        uint16_t arg = search_variable(mrb, irep->syms[b], bnest);
         if (arg != 0) {
           /* must replace */
-          irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+          irep->iseq[i] = OP_GETUPVAR;
+          irep->iseq[i+2] = arg >> 8;
+          irep->iseq[i+3] = arg & 0xff;
         }
       }
       break;
 
     case OP_MOVE:
+      a = PEEK_B(irep->iseq+i+1);
+      b = PEEK_B(irep->iseq+i+2);
       /* 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 (potential_upvar_p(irep->lv, b, argc, irep->nlocals)) {
+        uint16_t arg = search_variable(mrb, irep->lv[b - 1].name, bnest);
         if (arg != 0) {
           /* must replace */
-          irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+          irep->iseq[i] = insn = OP_GETUPVAR;
+          irep->iseq[i+2] = arg >> 8;
+          irep->iseq[i+3] = arg & 0xff;
         }
       }
       /* 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 (potential_upvar_p(irep->lv, a, argc, irep->nlocals)) {
+        uint16_t arg = search_variable(mrb, irep->lv[a - 1].name, bnest);
         if (arg != 0) {
           /* must replace */
-          irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_B(c)) | arg;
+          irep->iseq[i] = insn = OP_SETUPVAR;
+          irep->iseq[i+1] = (mrb_code)b;
+          irep->iseq[i+2] = arg >> 8;
+          irep->iseq[i+3] = arg & 0xff;
         }
       }
       break;
 
     case OP_GETUPVAR:
+      a = PEEK_B(irep->iseq+i+1);
+      b = PEEK_B(irep->iseq+i+2);
+      c = PEEK_B(irep->iseq+i+3);
       {
-        int lev = GETARG_C(c)+1;
+        int lev = 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 (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) {
+          uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
           if (arg != 0) {
             /* must replace */
-            irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+            irep->iseq[i] = OP_GETUPVAR;
+            irep->iseq[i+2] = arg >> 8;
+            irep->iseq[i+3] = arg & 0xff;
           }
         }
       }
       break;
 
     case OP_SETUPVAR:
+      a = PEEK_B(irep->iseq+i+1);
+      b = PEEK_B(irep->iseq+i+2);
+      c = PEEK_B(irep->iseq+i+3);
       {
-        int lev = GETARG_C(c)+1;
+        int lev = 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 (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) {
+          uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
           if (arg != 0) {
             /* must replace */
-            irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+            irep->iseq[i] = OP_SETUPVAR;
+            irep->iseq[i+1] = a;
+            irep->iseq[i+2] = arg >> 8;
+            irep->iseq[i+3] = arg & 0xff;
           }
         }
       }
       break;
 
-    case OP_STOP:
-      if (mrb->c->ci->acc >= 0) {
-        irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL);
-      }
-      break;
+    case OP_EXT1:
+      insn = PEEK_B(irep->iseq+i+1);
+      i += mrb_insn_size1[insn]+1;
+      continue;
+    case OP_EXT2:
+      insn = PEEK_B(irep->iseq+i+1);
+      i += mrb_insn_size2[insn]+1;
+      continue;
+    case OP_EXT3:
+      insn = PEEK_B(irep->iseq+i+1);
+      i += mrb_insn_size3[insn]+1;
+      continue;
     }
+    i+=mrb_insn_size[insn];
   }
 }
 
@@ -189,7 +226,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
   struct mrb_parser_state *p;
   struct RProc *proc;
   struct REnv *e;
-  mrb_callinfo *ci = &mrb->c->ci[-1]; /* callinfo of eval caller */
+  mrb_callinfo *ci; /* callinfo of eval caller */
   struct RClass *target_class = NULL;
   int bidx;
 
@@ -203,6 +240,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
   mrbc_filename(mrb, cxt, file ? file : "(eval)");
   cxt->capture_errors = TRUE;
   cxt->no_optimize = TRUE;
+  cxt->on_eval = TRUE;
 
   p = mrb_parse_nstring(mrb, s, len, cxt);
 
@@ -238,8 +276,16 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
     mrbc_context_free(mrb, cxt);
     mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
   }
-  target_class = MRB_PROC_TARGET_CLASS(ci->proc);
-  if (!MRB_PROC_CFUNC_P(ci->proc)) {
+  if (mrb->c->ci > mrb->c->cibase) {
+    ci = &mrb->c->ci[-1];
+  }
+  else {
+    ci = mrb->c->cibase;
+  }
+  if (ci->proc) {
+    target_class = MRB_PROC_TARGET_CLASS(ci->proc);
+  }
+  if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
     if (ci->env) {
       e = ci->env;
     }
@@ -254,6 +300,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
       if (ci->argc < 0) bidx = 2;
       else bidx += 1;
       MRB_ENV_SET_BIDX(e, bidx);
+      ci->env = e;
     }
     proc->e.env = e;
     proc->flags |= MRB_PROC_ENVSET;
@@ -276,10 +323,12 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
   /* no argument passed from eval() */
   mrb->c->ci->argc = 0;
   if (mrb->c->ci->acc < 0) {
+    ptrdiff_t cioff = mrb->c->ci - mrb->c->cibase;
     mrb_value ret = mrb_top_run(mrb, proc, self, 0);
     if (mrb->exc) {
       mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
     }
+    mrb->c->ci = mrb->c->cibase + cioff;
     return ret;
   }
   /* clear block */
@@ -325,6 +374,7 @@ f_instance_eval(mrb_state *mrb, mrb_value self)
     proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
     MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv));
     mrb_assert(!MRB_PROC_CFUNC_P(proc));
+    mrb->c->ci->target_class = mrb_class_ptr(cv);
     return exec_irep(mrb, self, proc);
   }
   else {
index 66ca1fc..4d7dd46 100644 (file)
@@ -34,7 +34,7 @@ assert('Kernel.eval', '15.3.1.2.3') do
   }
   assert_equal(2) {
     a = 10
-    Kernel.eval 'def f(a); b=a.send(:+, 1); end'
+    Kernel.eval 'def f(a); b=a+1; end'
     f(1)
   }
 end
@@ -58,7 +58,7 @@ end
 
 assert('String instance_eval') do
   obj = Object.new
-  obj.instance_variable_set :@test, 'test'
+  obj.instance_eval{ @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)}
@@ -99,3 +99,34 @@ assert('Object#instance_eval with begin-rescue-ensure execution order') do
   hell_raiser = HellRaiser.new
   assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
 end
+
+assert('Kernel#instance_eval() to define singleton methods Issue #3141') do
+  foo_class = Class.new do
+    def bar(x)
+      instance_eval "def baz; #{x}; end"
+    end
+  end
+
+  f1 = foo_class.new
+  f2 = foo_class.new
+  f1.bar 1
+  f2.bar 2
+  assert_equal(1){f1.baz}
+  assert_equal(2){f2.baz}
+end
+
+assert('Kernel.#eval(string) Issue #4021') do
+  assert_equal('FOO') { (eval <<'EOS').call }
+foo = "FOO"
+Proc.new { foo }
+EOS
+  assert_equal('FOO') {
+    def do_eval(code)
+      eval(code)
+    end
+    do_eval(<<'EOS').call
+foo = "FOO"
+Proc.new { foo }
+EOS
+  }
+end
index 83153a9..17ce77c 100644 (file)
@@ -127,7 +127,6 @@ fiber_init(mrb_state *mrb, mrb_value self)
   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 */
 
@@ -175,6 +174,9 @@ fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
 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;
 }
@@ -184,26 +186,30 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
 {
   struct mrb_context *c = fiber_check(mrb, self);
   struct mrb_context *old_c = mrb->c;
+  enum mrb_fiber_state status;
   mrb_value value;
 
   fiber_check_cfunc(mrb, c);
-  if (resume && c->status == MRB_FIBER_TRANSFERRED) {
+  status = c->status;
+  if (resume && 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 (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) {
+    mrb_raise(mrb, E_FIBER_ERROR, "double resume");
   }
-  if (c->status == MRB_FIBER_TERMINATED) {
+  if (status == MRB_FIBER_TERMINATED) {
     mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
   }
-  mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
+  old_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) {
+  fiber_switch_context(mrb, c);
+  if (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");
+    if (!c->ci->proc) {
+      mrb_raise(mrb, E_FIBER_ERROR, "double resume (current)");
     }
+    mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
     b = c->stack+1;
     e = b + len;
     while (b<e) {
@@ -215,7 +221,6 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
   else {
     value = fiber_result(mrb, a, len);
   }
-  fiber_switch_context(mrb, c);
 
   if (vmexec) {
     c->vmexec = TRUE;
index 549bca0..547f340 100644 (file)
@@ -27,9 +27,9 @@ class Hash
     length = object.length
     if length == 1
       o = object[0]
-      if o.respond_to?(:to_hash)
+      if Hash === o
         h = self.new
-        object[0].to_hash.each { |k, v| h[k] = v }
+        o.each { |k, v| h[k] = v }
         return h
       elsif o.respond_to?(:to_a)
         h = self.new
@@ -62,25 +62,6 @@ class Hash
 
   ##
   # 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
   #
@@ -101,7 +82,7 @@ class Hash
   #
 
   def merge!(other, &block)
-    raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
+    raise TypeError, "Hash required (#{other.class} given)" unless Hash === other
     if block
       other.each_key{|k|
         self[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k]
@@ -116,6 +97,31 @@ class Hash
 
   ##
   # call-seq:
+  #   hsh.compact!    -> hsh
+  #
+  # Removes all nil values from the hash. Returns the hash.
+  # Returns nil if the hash does not contain nil values.
+  #
+  #   h = { a: 1, b: false, c: nil }
+  #   h.compact!     #=> { a: 1, b: false }
+  #
+
+  def compact!
+    keys = self.keys
+    nk = keys.select{|k|
+      self[k] != nil
+    }
+    return nil if (keys.size == nk.size)
+    h = {}
+    nk.each {|k|
+      h[k] = self[k]
+    }
+    h
+    self.replace(h)
+  end
+
+  ##
+  # call-seq:
   #    hsh.compact     -> new_hsh
   #
   # Returns a new hash with the nil values/key pairs removed
@@ -125,9 +131,13 @@ class Hash
   #    h             #=> { a: 1, b: false, c: nil }
   #
   def compact
-    result = self.dup
-    result.compact!
-    result
+    h = {}
+    self.keys.select{|k|
+      self[k] != nil
+    }.each {|k|
+      h[k] = self[k]
+    }
+    h
   end
 
   ##
@@ -300,11 +310,7 @@ class Hash
   #     h1 < h1    #=> false
   #
   def <(hash)
-    begin
-      hash = hash.to_hash
-    rescue NoMethodError
-      raise TypeError, "can't convert #{hash.class} to Hash"
-    end
+    raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
     size < hash.size and all? {|key, val|
       hash.key?(key) and hash[key] == val
     }
@@ -324,11 +330,7 @@ class Hash
   #     h1 <= h1   #=> true
   #
   def <=(hash)
-    begin
-      hash = hash.to_hash
-    rescue NoMethodError
-      raise TypeError, "can't convert #{hash.class} to Hash"
-    end
+    raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
     size <= hash.size and all? {|key, val|
       hash.key?(key) and hash[key] == val
     }
@@ -348,11 +350,7 @@ class Hash
   #     h1 > h1    #=> false
   #
   def >(hash)
-    begin
-      hash = hash.to_hash
-    rescue NoMethodError
-      raise TypeError, "can't convert #{hash.class} to Hash"
-    end
+    raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
     size > hash.size and hash.all? {|key, val|
       key?(key) and self[key] == val
     }
@@ -372,11 +370,7 @@ class Hash
   #     h1 >= h1   #=> true
   #
   def >=(hash)
-    begin
-      hash = hash.to_hash
-    rescue NoMethodError
-      raise TypeError, "can't convert #{hash.class} to Hash"
-    end
+    raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
     size >= hash.size and hash.all? {|key, val|
       key?(key) and self[key] == val
     }
@@ -432,9 +426,9 @@ class Hash
     return to_enum :transform_keys! unless block
     self.keys.each do |k|
       value = self[k]
-      new_key = block.call(k)
       self.__delete(k)
-      self[new_key] = value
+      k = block.call(k) if block
+      self[k] = value
     end
     self
   end
@@ -457,6 +451,7 @@ class Hash
     end
     hash
   end
+
   ##
   # call-seq:
   #    hsh.transform_values! {|key| block } -> hsh
index 6619f52..e611266 100644 (file)
@@ -37,42 +37,6 @@ hash_values_at(mrb_state *mrb, mrb_value hash)
 }
 
 /*
- * call-seq:
- *   hsh.compact!    -> hsh
- *
- * Removes all nil values from the hash. Returns the hash.
- *
- *   h = { a: 1, b: false, c: nil }
- *   h.compact!     #=> { a: 1, b: false }
- */
-static mrb_value
-hash_compact_bang(mrb_state *mrb, mrb_value hash)
-{
-  khiter_t k;
-  khash_t(ht) *h = RHASH_TBL(hash);
-  mrb_int n = -1;
-
-  if (!h) return mrb_nil_value();
-  for (k = kh_begin(h); k != kh_end(h); k++) {
-    if (kh_exist(h, k)) {
-      mrb_value val = kh_value(h, k).v;
-      khiter_t k2;
-
-      if (mrb_nil_p(val)) {
-        kh_del(ht, mrb, h, k);
-        n = kh_value(h, k).n;
-        for (k2 = kh_begin(h); k2 != kh_end(h); k2++) {
-          if (!kh_exist(h, k2)) continue;
-          if (kh_value(h, k2).n > n) kh_value(h, k2).n--;
-        }
-      }
-    }
-  }
-  if (n < 0) return mrb_nil_value();
-  return hash;
-}
-
-/*
  *  call-seq:
  *     hsh.slice(*keys) -> a_hash
  *
@@ -85,28 +49,22 @@ hash_compact_bang(mrb_state *mrb, mrb_value hash)
 static mrb_value
 hash_slice(mrb_state *mrb, mrb_value hash)
 {
-  khash_t(ht) *h = RHASH_TBL(hash);
   mrb_value *argv, result;
   mrb_int argc, i;
-  khiter_t k;
-  int ai;
 
   mrb_get_args(mrb, "*", &argv, &argc);
-  if (argc == 0 || h == NULL) {
+  if (argc == 0) {
     return mrb_hash_new_capa(mrb, argc);
   }
   result = mrb_hash_new_capa(mrb, argc);
-  ai = mrb_gc_arena_save(mrb);
   for (i = 0; i < argc; i++) {
     mrb_value key = argv[i];
+    mrb_value val;
 
-    k = kh_get(ht, mrb, h, key);
-    if (k != kh_end(h)) {
-      mrb_value val = kh_value(h, k).v;
-
+    val = mrb_hash_fetch(mrb, hash, key, mrb_undef_value());
+    if (!mrb_undef_p(val)) {
       mrb_hash_set(mrb, result, key, val);
     }
-    mrb_gc_arena_restore(mrb, ai);
   }
   return result;
 }
@@ -118,7 +76,6 @@ mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
 
   h = mrb->hash_class;
   mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY());
-  mrb_define_method(mrb, h, "compact!",  hash_compact_bang, MRB_ARGS_NONE());
   mrb_define_method(mrb, h, "slice",     hash_slice, MRB_ARGS_ANY());
 }
 
index 269da80..b5d0aaa 100644 (file)
@@ -45,12 +45,6 @@ assert('Hash.[] for sub class') do
   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' }
index 0baaab6..51804ae 100644 (file)
@@ -11,17 +11,19 @@ istruct_test_initialize(mrb_state *mrb, mrb_value self)
   mrb_value object;
   mrb_get_args(mrb, "o", &object);
 
-  if (mrb_float_p(object))
-  {
-    snprintf(string, size, "float(%.3f)", mrb_float(object));
+  if (mrb_fixnum_p(object)) {
+    strncpy(string, "fixnum", size-1);
   }
-  else if (mrb_fixnum_p(object))
-  {
-    snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object));
+#ifndef MRB_WITHOUT_FLOAT
+  else if (mrb_float_p(object)) {
+    strncpy(string, "float", size-1);
   }
-  else if (mrb_string_p(object))
-  {
-    snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
+#endif
+  else if (mrb_string_p(object)) {
+    strncpy(string, "string", size-1);
+  }
+  else {
+    strncpy(string, "anything", size-1);
   }
 
   string[size - 1] = 0; // force NULL at the end
@@ -47,7 +49,7 @@ istruct_test_test_receive(mrb_state *mrb, mrb_value self)
   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");
+    mrb_raise(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
   }
   return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's');
 }
index 4958592..f959a17 100644 (file)
@@ -17,14 +17,14 @@ end
 
 assert('InlineStructTest#dup') do
   obj = InlineStructTest.new(1)
-  assert_equal obj.to_s, 'fixnum(1)'
-  assert_equal obj.dup.to_s, 'fixnum(1)'
+  assert_equal obj.to_s, 'fixnum'
+  assert_equal obj.dup.to_s, 'fixnum'
 end
 
 assert('InlineStructTest#clone') do
   obj = InlineStructTest.new(1)
-  assert_equal obj.to_s, 'fixnum(1)'
-  assert_equal obj.clone.to_s, 'fixnum(1)'
+  assert_equal obj.to_s, 'fixnum'
+  assert_equal obj.clone.to_s, 'fixnum'
 end
 
 assert('InlineStruct#object_id') do
@@ -38,22 +38,22 @@ end
 
 assert('InlineStructTest#mutate (dup)') do
   obj1 = InlineStructTest.new("foo")
-  assert_equal obj1.to_s, "string(foo)"
+  assert_equal obj1.to_s, "string"
   obj2 = obj1.dup
-  assert_equal obj2.to_s, "string(foo)"
+  assert_equal obj2.to_s, "string"
   obj1.mutate
-  assert_equal obj1.to_s, "mutate(foo)"
-  assert_equal obj2.to_s, "string(foo)"
+  assert_equal obj1.to_s, "mutate"
+  assert_equal obj2.to_s, "string"
 end
 
 assert('InlineStructTest#mutate (clone)') do
   obj1 = InlineStructTest.new("foo")
-  assert_equal obj1.to_s, "string(foo)"
+  assert_equal obj1.to_s, "string"
   obj2 = obj1.clone
-  assert_equal obj2.to_s, "string(foo)"
+  assert_equal obj2.to_s, "string"
   obj1.mutate
-  assert_equal obj1.to_s, "mutate(foo)"
-  assert_equal obj2.to_s, "string(foo)"
+  assert_equal obj1.to_s, "mutate"
+  assert_equal obj2.to_s, "string"
 end
 
 assert('InlineStructTest#test_receive(string)') do
@@ -101,26 +101,6 @@ 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
@@ -128,24 +108,11 @@ if InlineStructTest.length == 12
   assert('InlineStructTest length [32 bit]') do
     assert_equal InlineStructTest.length, 3 * 4
   end
+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"
+# 16-bit mode
+if InlineStructTest.length == 6
+  assert('InlineStructTest length [16 bit]') do
+    assert_equal InlineStructTest.length, 3 * 2
   end
 end
index 50fa496..d799645 100644 (file)
@@ -4,7 +4,7 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
   spec.summary = 'IO and File class'
 
   spec.cc.include_paths << "#{build.root}/src"
-  
+
   case RUBY_PLATFORM
   when /mingw|mswin/
     spec.linker.libraries += ['Ws2_32']
@@ -14,4 +14,5 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
   if build.kind_of?(MRuby::CrossBuild) && %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target)
     spec.linker.libraries += ['ws2_32']
   end
+  spec.add_test_dependency 'mruby-time', core: 'mruby-time'
 end
index 3dcfe3a..c006634 100644 (file)
@@ -114,11 +114,13 @@ mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
 
   mrb_get_args(mrb, "*", &argv, &argc);
   for (i = 0; i < argc; i++) {
-    pathv = mrb_convert_type(mrb, argv[i], MRB_TT_STRING, "String", "to_str");
-    path = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &pathv), -1);
+    const char *utf8_path;
+    pathv = mrb_ensure_string_type(mrb, argv[i]);
+    utf8_path = mrb_string_value_cstr(mrb, &pathv);
+    path = mrb_locale_from_utf8(utf8_path, -1);
     if (UNLINK(path) < 0) {
       mrb_locale_free(path);
-      mrb_sys_fail(mrb, path);
+      mrb_sys_fail(mrb, utf8_path);
     }
     mrb_locale_free(path);
   }
@@ -310,7 +312,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
   }
   home = mrb_locale_from_utf8(home, -1);
   path = mrb_str_new_cstr(mrb, home);
-  mrb_utf8_free(home);
+  mrb_locale_free(home);
   return path;
 #else
   argc = mrb_get_argc(mrb);
@@ -327,7 +329,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
   }
   home = mrb_locale_from_utf8(home, -1);
   path = mrb_str_new_cstr(mrb, home);
-  mrb_utf8_free(home);
+  mrb_locale_free(home);
   return path;
 #endif
 }
@@ -343,7 +345,7 @@ mrb_file_mtime(mrb_state *mrb, mrb_value self)
   fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
   if (fstat(fd, &st) == -1)
     return mrb_false_value();
-  return mrb_funcall(mrb, obj, "at", 1, mrb_float_value(mrb, st.st_mtime));
+  return mrb_funcall(mrb, obj, "at", 1, mrb_fixnum_value(st.st_mtime));
 }
 
 mrb_value
@@ -415,10 +417,11 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) {
 
   mrb_get_args(mrb, "i*", &mode, &filenames, &argc);
   for (i = 0; i < argc; i++) {
-    char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, filenames[i]), -1);
+    const char *utf8_path = mrb_str_to_cstr(mrb, filenames[i]);
+    char *path = mrb_locale_from_utf8(utf8_path, -1);
     if (CHMOD(path, mode) == -1) {
       mrb_locale_free(path);
-      mrb_sys_fail(mrb, path);
+      mrb_sys_fail(mrb, utf8_path);
     }
     mrb_locale_free(path);
   }
index bc17dd9..e5b83e9 100644 (file)
@@ -80,6 +80,9 @@ io_get_open_fptr(mrb_state *mrb, mrb_value self)
   struct mrb_io *fptr;
 
   fptr = (struct mrb_io *)mrb_get_datatype(mrb, self, &mrb_io_type);
+  if (fptr == NULL) {
+    mrb_raise(mrb, E_IO_ERROR, "uninitialized stream.");
+  }
   if (fptr->fd < 0) {
     mrb_raise(mrb, E_IO_ERROR, "closed stream.");
   }
@@ -206,7 +209,7 @@ mrb_fd_cloexec(mrb_state *mrb, int fd)
 #endif
 }
 
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
 static int
 mrb_cloexec_pipe(mrb_state *mrb, int fildes[2])
 {
@@ -302,7 +305,112 @@ option_to_fd(mrb_state *mrb, mrb_value obj, const char *key)
   return -1; /* never reached */
 }
 
-#ifndef _WIN32
+#ifdef _WIN32
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value cmd, io;
+  mrb_value mode = mrb_str_new_cstr(mrb, "r");
+  mrb_value opt  = mrb_hash_new(mrb);
+
+  struct mrb_io *fptr;
+  const char *pname;
+  int pid = 0, flags;
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+  SECURITY_ATTRIBUTES saAttr;
+
+  HANDLE ifd[2];
+  HANDLE ofd[2];
+
+  int doexec;
+  int opt_in, opt_out, opt_err;
+
+  ifd[0] = INVALID_HANDLE_VALUE;
+  ifd[1] = INVALID_HANDLE_VALUE;
+  ofd[0] = INVALID_HANDLE_VALUE;
+  ofd[1] = INVALID_HANDLE_VALUE;
+
+  mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
+  io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+
+  pname = mrb_string_value_cstr(mrb, &cmd);
+  flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+
+  doexec = (strcmp("-", pname) != 0);
+  opt_in = option_to_fd(mrb, opt, "in");
+  opt_out = option_to_fd(mrb, opt, "out");
+  opt_err = option_to_fd(mrb, opt, "err");
+
+  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+  saAttr.bInheritHandle = TRUE;
+  saAttr.lpSecurityDescriptor = NULL;
+
+  if (flags & FMODE_READABLE) {
+    if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
+        || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
+      mrb_sys_fail(mrb, "pipe");
+    }
+  }
+
+  if (flags & FMODE_WRITABLE) {
+    if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
+        || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
+      mrb_sys_fail(mrb, "pipe");
+    }
+  }
+
+  if (doexec) {
+    ZeroMemory(&pi, sizeof(pi));
+    ZeroMemory(&si, sizeof(si));
+    si.cb = sizeof(si);
+    si.dwFlags |= STARTF_USESHOWWINDOW;
+    si.wShowWindow = SW_HIDE;
+    si.dwFlags |= STARTF_USESTDHANDLES;
+    if (flags & FMODE_READABLE) {
+      si.hStdOutput = ofd[1];
+      si.hStdError = ofd[1];
+    }
+    if (flags & FMODE_WRITABLE) {
+      si.hStdInput = ifd[0];
+    }
+    if (!CreateProcess(
+        NULL, (char*)pname, NULL, NULL,
+        TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
+      CloseHandle(ifd[0]);
+      CloseHandle(ifd[1]);
+      CloseHandle(ofd[0]);
+      CloseHandle(ofd[1]);
+      mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+    }
+    CloseHandle(pi.hThread);
+    CloseHandle(ifd[0]);
+    CloseHandle(ofd[1]);
+    pid = pi.dwProcessId;
+  }
+
+  mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+
+  fptr = mrb_io_alloc(mrb);
+  fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
+  fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
+  fptr->pid = pid;
+  fptr->readable = ((flags & FMODE_READABLE) != 0);
+  fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+  fptr->sync = 0;
+
+  DATA_TYPE(io) = &mrb_io_type;
+  DATA_PTR(io)  = fptr;
+  return io;
+}
+#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+  mrb_raise(mrb, E_NOTIMP_ERROR, "IO#popen is not supported on the platform");
+  return mrb_false_value();
+}
+#else
 mrb_value
 mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
 {
@@ -437,104 +545,6 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
   }
   return result;
 }
-#else
-mrb_value
-mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
-{
-  mrb_value cmd, io;
-  mrb_value mode = mrb_str_new_cstr(mrb, "r");
-  mrb_value opt  = mrb_hash_new(mrb);
-
-  struct mrb_io *fptr;
-  const char *pname;
-  int pid = 0, flags;
-  STARTUPINFO si;
-  PROCESS_INFORMATION pi;
-  SECURITY_ATTRIBUTES saAttr;
-
-  HANDLE ifd[2];
-  HANDLE ofd[2];
-
-  int doexec;
-  int opt_in, opt_out, opt_err;
-
-  ifd[0] = INVALID_HANDLE_VALUE;
-  ifd[1] = INVALID_HANDLE_VALUE;
-  ofd[0] = INVALID_HANDLE_VALUE;
-  ofd[1] = INVALID_HANDLE_VALUE;
-
-  mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
-  io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
-
-  pname = mrb_string_value_cstr(mrb, &cmd);
-  flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
-
-  doexec = (strcmp("-", pname) != 0);
-  opt_in = option_to_fd(mrb, opt, "in");
-  opt_out = option_to_fd(mrb, opt, "out");
-  opt_err = option_to_fd(mrb, opt, "err");
-
-  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
-  saAttr.bInheritHandle = TRUE;
-  saAttr.lpSecurityDescriptor = NULL;
-
-  if (flags & FMODE_READABLE) {
-    if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
-        || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
-      mrb_sys_fail(mrb, "pipe");
-    }
-  }
-
-  if (flags & FMODE_WRITABLE) {
-    if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
-        || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
-      mrb_sys_fail(mrb, "pipe");
-    }
-  }
-
-  if (doexec) {
-    ZeroMemory(&pi, sizeof(pi));
-    ZeroMemory(&si, sizeof(si));
-    si.cb = sizeof(si);
-    si.dwFlags |= STARTF_USESHOWWINDOW;
-    si.wShowWindow = SW_HIDE;
-    si.dwFlags |= STARTF_USESTDHANDLES;
-    if (flags & FMODE_READABLE) {
-      si.hStdOutput = ofd[1];
-      si.hStdError = ofd[1];
-    }
-    if (flags & FMODE_WRITABLE) {
-      si.hStdInput = ifd[0];
-    }
-    if (!CreateProcess(
-        NULL, (char*)pname, NULL, NULL,
-        TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
-      CloseHandle(ifd[0]);
-      CloseHandle(ifd[1]);
-      CloseHandle(ofd[0]);
-      CloseHandle(ofd[1]);
-      mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
-    }
-    CloseHandle(pi.hThread);
-    CloseHandle(ifd[0]);
-    CloseHandle(ofd[1]);
-    pid = pi.dwProcessId;
-  }
-
-  mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
-
-  fptr = mrb_io_alloc(mrb);
-  fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
-  fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
-  fptr->pid = pid;
-  fptr->readable = ((flags & FMODE_READABLE) != 0);
-  fptr->writable = ((flags & FMODE_WRITABLE) != 0);
-  fptr->sync = 0;
-
-  DATA_TYPE(io) = &mrb_io_type;
-  DATA_PTR(io)  = fptr;
-  return io;
-}
 #endif
 
 static int
@@ -777,7 +787,7 @@ reopen:
     mrb_str_modify(mrb, mrb_str_ptr(emsg));
     mrb_sys_fail(mrb, RSTRING_PTR(emsg));
   }
-  mrb_utf8_free(fname);
+  mrb_locale_free(fname);
 
   if (fd <= 2) {
     mrb_fd_cloexec(mrb, fd);
@@ -873,7 +883,7 @@ mrb_io_sysseek(mrb_state *mrb, mrb_value io)
     whence = 0;
   }
 
-  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  fptr = io_get_open_fptr(mrb, io);
   pos = lseek(fptr->fd, (off_t)offset, (int)whence);
   if (pos == -1) {
     mrb_sys_fail(mrb, "sysseek");
@@ -896,7 +906,7 @@ mrb_io_syswrite(mrb_state *mrb, mrb_value io)
   mrb_value str, buf;
   int fd, length;
 
-  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  fptr = io_get_open_fptr(mrb, io);
   if (! fptr->writable) {
     mrb_raise(mrb, E_IO_ERROR, "not opened for writing");
   }
@@ -946,7 +956,7 @@ mrb_io_closed(mrb_state *mrb, mrb_value io)
 {
   struct mrb_io *fptr;
   fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
-  if (fptr->fd >= 0) {
+  if (fptr == NULL || fptr->fd >= 0) {
     return mrb_false_value();
   }
 
@@ -957,7 +967,7 @@ mrb_value
 mrb_io_pid(mrb_state *mrb, mrb_value io)
 {
   struct mrb_io *fptr;
-  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  fptr = io_get_open_fptr(mrb, io);
 
   if (fptr->pid > 0) {
     return mrb_fixnum_value(fptr->pid);
@@ -1001,7 +1011,7 @@ mrb_io_read_data_pending(mrb_state *mrb, mrb_value io)
   return 0;
 }
 
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
 static mrb_value
 mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
 {
@@ -1086,7 +1096,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
     FD_ZERO(rp);
     for (i = 0; i < RARRAY_LEN(read); i++) {
       read_io = RARRAY_PTR(read)[i];
-      fptr = (struct mrb_io *)mrb_get_datatype(mrb, read_io, &mrb_io_type);
+      fptr = io_get_open_fptr(mrb, read_io);
       FD_SET(fptr->fd, rp);
       if (mrb_io_read_data_pending(mrb, read_io)) {
         pending++;
@@ -1108,7 +1118,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
     wp = &wset;
     FD_ZERO(wp);
     for (i = 0; i < RARRAY_LEN(write); i++) {
-      fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(write)[i], &mrb_io_type);
+      fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
       FD_SET(fptr->fd, wp);
       if (max < fptr->fd)
         max = fptr->fd;
@@ -1127,7 +1137,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
     ep = &eset;
     FD_ZERO(ep);
     for (i = 0; i < RARRAY_LEN(except); i++) {
-      fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(except)[i], &mrb_io_type);
+      fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
       FD_SET(fptr->fd, ep);
       if (max < fptr->fd)
         max = fptr->fd;
@@ -1165,7 +1175,7 @@ retry:
     if (rp) {
       list = RARRAY_PTR(result)[0];
       for (i = 0; i < RARRAY_LEN(read); i++) {
-        fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(read)[i], &mrb_io_type);
+        fptr = io_get_open_fptr(mrb, RARRAY_PTR(read)[i]);
         if (FD_ISSET(fptr->fd, rp) ||
             FD_ISSET(fptr->fd, &pset)) {
           mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]);
@@ -1176,7 +1186,7 @@ retry:
     if (wp) {
       list = RARRAY_PTR(result)[1];
       for (i = 0; i < RARRAY_LEN(write); i++) {
-        fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(write)[i], &mrb_io_type);
+        fptr = io_get_open_fptr(mrb, RARRAY_PTR(write)[i]);
         if (FD_ISSET(fptr->fd, wp)) {
           mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
         } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, wp)) {
@@ -1188,7 +1198,7 @@ retry:
     if (ep) {
       list = RARRAY_PTR(result)[2];
       for (i = 0; i < RARRAY_LEN(except); i++) {
-        fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(except)[i], &mrb_io_type);
+        fptr = io_get_open_fptr(mrb, RARRAY_PTR(except)[i]);
         if (FD_ISSET(fptr->fd, ep)) {
           mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
         } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, ep)) {
@@ -1205,7 +1215,7 @@ mrb_value
 mrb_io_fileno(mrb_state *mrb, mrb_value io)
 {
   struct mrb_io *fptr;
-  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  fptr = io_get_open_fptr(mrb, io);
   return mrb_fixnum_value(fptr->fd);
 }
 
@@ -1303,7 +1313,7 @@ mrb_init_io(mrb_state *mrb)
   mrb_define_class_method(mrb, io, "for_fd",  mrb_io_s_for_fd,   MRB_ARGS_ANY());
   mrb_define_class_method(mrb, io, "select",  mrb_io_s_select,  MRB_ARGS_ANY());
   mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ANY());
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
   mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE());
 #endif
 
index dc6fe36..ba41004 100644 (file)
@@ -1,16 +1,16 @@
 ##
-# IO Test
+# File Test
 
-assert('File', '15.2.21') do
-  File.class == Class
+assert('File TEST SETUP') do
+  MRubyIOTestUtil.io_test_setup
 end
 
-assert('File', '15.2.21.2') do
-  File.superclass == IO
+assert('File', '15.2.21') do
+  assert_equal Class, File.class
 end
 
-assert('File TEST SETUP') do
-  MRubyIOTestUtil.io_test_setup
+assert('File', '15.2.21.2') do
+  assert_equal IO, File.superclass
 end
 
 assert('File#initialize', '15.2.21.4.1') do
@@ -27,7 +27,7 @@ assert('File#path', '15.2.21.4.2') do
   assert_equal $mrbtest_io_rfname, io.path
   io.close
   assert_equal $mrbtest_io_rfname, io.path
-  io.closed?
+  assert_true io.closed?
 end
 
 assert('File.basename') do
@@ -69,18 +69,15 @@ assert('File#flock') do
 end
 
 assert('File#mtime') do
-  unless Object.const_defined?(:Time)
-    skip "File#mtime require Time"
-  end
   begin
-    now = Time.now.to_i
-    mt = 0
-    File.open('mtime-test', 'w') do |f|
-      mt = f.mtime.to_i
+    File.open("#{$mrbtest_io_wfname}.mtime", 'w') do |f|
+      assert_equal Time, f.mtime.class
+      File.open("#{$mrbtest_io_wfname}.mtime", 'r') do |f2|
+        assert_equal true, f.mtime == f2.mtime
+      end
     end
-    assert_equal true, mt >= now
   ensure
-    File.delete('mtime-test')
+    File.delete("#{$mrbtest_io_wfname}.mtime")
   end
 end
 
@@ -177,7 +174,6 @@ assert('File.path') do
   assert_equal "a/../b/./c", File.path("a/../b/./c")
   assert_raise(TypeError) { File.path(nil) }
   assert_raise(TypeError) { File.path(123) }
-
 end
 
 assert('File.symlink') do
@@ -200,11 +196,11 @@ assert('File.symlink') do
 end
 
 assert('File.chmod') do
-  File.open('chmod-test', 'w') {}
+  File.open("#{$mrbtest_io_wfname}.chmod-test", 'w') {}
   begin
-    assert_equal 1, File.chmod(0400, 'chmod-test')
+    assert_equal 1, File.chmod(0400, "#{$mrbtest_io_wfname}.chmod-test")
   ensure
-    File.delete('chmod-test')
+    File.delete("#{$mrbtest_io_wfname}.chmod-test")
   end
 end
 
index 2c831f0..04e10e0 100644 (file)
@@ -67,11 +67,11 @@ assert("FileTest.size?") do
   assert_raise IOError do
     FileTest.size?(fp1)
   end
+  assert_true fp1.closed?
   assert_raise IOError do
     FileTest.size?(fp2)
   end
-
-  fp1.closed? && fp2.closed?
+  assert_true fp2.closed?
 end
 
 assert("FileTest.socket?") do
@@ -105,11 +105,11 @@ assert("FileTest.zero?") do
   assert_raise IOError do
     FileTest.zero?(fp1)
   end
+  assert_true fp1.closed?
   assert_raise IOError do
     FileTest.zero?(fp2)
   end
-
-  fp1.closed? && fp2.closed?
+  assert_true fp2.closed?
 end
 
 assert('FileTest TEST CLEANUP') do
diff --git a/third-party/mruby/mrbgems/mruby-io/test/gc_filedes.sh b/third-party/mruby/mrbgems/mruby-io/test/gc_filedes.sh
deleted file mode 100644 (file)
index 6e5d1bb..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-ulimit -n 20
-mruby -e '100.times { File.open "'$0'" }'
index e06b149..85852c1 100644 (file)
@@ -1,26 +1,6 @@
 ##
 # IO Test
 
-unless Object.respond_to? :assert_nothing_raised
-  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
-end
-
 assert('IO TEST SETUP') do
   MRubyIOTestUtil.io_test_setup
   $cr = MRubyIOTestUtil.win? ? 1 : 0  # "\n" include CR or not
@@ -35,7 +15,7 @@ assert('IO', '15.2.20.2') do
 end
 
 assert('IO', '15.2.20.3') do
-  assert_include(IO.included_modules, Enumerable)
+  assert_include(IO.ancestors, Enumerable)
 end
 
 assert('IO.open', '15.2.20.4.1') do
@@ -50,8 +30,6 @@ assert('IO.open', '15.2.20.4.1') do
   IO.open(fd) do |io|
     assert_equal $mrbtest_io_msg, io.read
   end
-
-  true
 end
 
 assert('IO#close', '15.2.20.5.1') do
@@ -92,8 +70,6 @@ assert('IO#eof?', '15.2.20.5.6') do
   io.read
   assert_true io.eof?
   io.close
-
-  true
 end
 
 assert('IO#flush', '15.2.20.5.7') do
@@ -113,7 +89,6 @@ assert('IO#getc', '15.2.20.5.8') do
   }
   assert_equal nil, io.getc
   io.close
-  true
 end
 
 #assert('IO#gets', '15.2.20.5.9') do
@@ -199,8 +174,6 @@ assert('IO#write', '15.2.20.5.20') do
   io.rewind
   assert_equal "ab123fg", io.read
   io.close
-
-  true
 end
 
 assert('IO#<<') do
@@ -208,7 +181,6 @@ assert('IO#<<') do
   io << "" << ""
   assert_equal 0, io.pos
   io.close
-  true
 end
 
 assert('IO#dup for readable') do
@@ -228,7 +200,6 @@ assert('IO#dup for readable') do
   dup.close
   assert_false io.closed?
   io.close
-  true
 end
 
 assert('IO#dup for writable') do
@@ -241,7 +212,6 @@ assert('IO#dup for writable') do
   assert_equal "mruby", dup.sysread(5)
   dup.close
   io.close
-  true
 end
 
 assert('IO.for_fd') do
@@ -249,13 +219,11 @@ assert('IO.for_fd') do
   io = IO.for_fd(fd)
     assert_equal $mrbtest_io_msg, io.read
   io.close
-  true
 end
 
 assert('IO.new') do
   io = IO.new(0)
   io.close
-  true
 end
 
 assert('IO gc check') do
@@ -300,7 +268,6 @@ assert('IO.sysopen, IO#sysread') do
   io = IO.new fd, "w"
   assert_raise(IOError) { io.sysread(1) }
   io.close
-  true
 end
 
 assert('IO.sysopen, IO#syswrite') do
@@ -314,8 +281,6 @@ assert('IO.sysopen, IO#syswrite') do
   io = IO.new(IO.sysopen($mrbtest_io_rfname), "r")
   assert_raise(IOError) { io.syswrite("a") }
   io.close
-
-  true
 end
 
 assert('IO#_read_buf') do
@@ -339,20 +304,25 @@ assert('IO#_read_buf') do
   assert_equal true, io.eof
   assert_equal true, io.eof?
   io.close
-  io.closed?
 end
 
 assert('IO#isatty') do
   skip "isatty is not supported on this platform" if MRubyIOTestUtil.win?
-  f1 = File.open("/dev/tty")
-  f2 = File.open($mrbtest_io_rfname)
-
-  assert_true  f1.isatty
-  assert_false f2.isatty
-
-  f1.close
-  f2.close
-  true
+  begin
+    f = File.open("/dev/tty")
+  rescue RuntimeError => e
+    skip e.message
+  else
+    assert_true f.isatty
+  ensure
+    f&.close
+  end
+  begin
+    f = File.open($mrbtest_io_rfname)
+    assert_false f.isatty
+  ensure
+    f&.close
+  end
 end
 
 assert('IO#pos=, IO#seek') do
@@ -366,7 +336,6 @@ assert('IO#pos=, IO#seek') do
   assert_equal 0, io.seek(0)
   assert_equal 0, io.pos
   io.close
-  io.closed?
 end
 
 assert('IO#rewind') do
@@ -377,7 +346,6 @@ assert('IO#rewind') do
   assert_equal 0, io.rewind
   assert_equal 0, io.pos
   io.close
-  io.closed?
 end
 
 assert('IO#gets') do
@@ -426,7 +394,6 @@ assert('IO#gets') do
   assert_equal nil, io.gets, "gets third line; returns nil"
 
   io.close
-  io.closed?
 end
 
 assert('IO#gets - paragraph mode') do
@@ -437,7 +404,6 @@ assert('IO#gets - paragraph mode') do
   io.write "2" * 10 + "\n"
   assert_equal 34 + $cr * 4, io.pos
   io.close
-  assert_equal true, io.closed?
 
   fd = IO.sysopen $mrbtest_io_wfname
   io = IO.new fd
@@ -448,7 +414,6 @@ assert('IO#gets - paragraph mode') do
   text2 = io.gets("")
   assert_equal para2, text2
   io.close
-  io.closed?
 end
 
 assert('IO.popen') do
@@ -542,7 +507,6 @@ assert('IO#fileno') do
   assert_equal io.fileno, fd
   assert_equal io.to_i, fd
   io.close
-  io.closed?
 end
 
 assert('IO#close_on_exec') do
@@ -564,7 +528,6 @@ assert('IO#close_on_exec') do
   assert_equal(false, io.close_on_exec?)
 
   io.close
-  io.closed?
 
   begin
     r, w = IO.pipe
index 25a4d4e..bf739ed 100644 (file)
@@ -1,6 +1,7 @@
 module Kernel
   # call-seq:
   #   obj.yield_self {|_obj|...} -> an_object
+  #   obj.then {|_obj|...}       -> an_object
   #
   # Yields <i>obj</i> and returns the result.
   #
@@ -10,4 +11,5 @@ module Kernel
     return to_enum :yield_self unless block
     block.call(self)
   end
+  alias then yield_self
 end
index 32d8637..99affbf 100644 (file)
@@ -93,9 +93,8 @@ mrb_f_method(mrb_state *mrb, mrb_value self)
  *  (<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.
+ *  <code>String#to_i</code>.  Non string values will be treated as integers.
+ *  Passing <code>nil</code> raises a TypeError.
  *
  *     Integer(123.999)    #=> 123
  *     Integer("0x1a")     #=> 26
@@ -142,8 +141,7 @@ mrb_f_float(mrb_state *mrb, mrb_value self)
  *     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.
+ *  converted using <code>to_s</code> method.
  *
  *     String(self)        #=> "main"
  *     String(self.class)  #=> "Object"
@@ -155,10 +153,7 @@ 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");
-  }
+  tmp = mrb_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
   return tmp;
 }
 
@@ -166,9 +161,7 @@ mrb_f_string(mrb_state *mrb, mrb_value self)
  *  call-seq:
  *     Array(arg)    -> array
  *
- *  Returns +arg+ as an Array.
- *
- *  First tries to call Array#to_ary on +arg+, then Array#to_a.
+ *  Returns +arg+ as an Array using to_a method.
  *
  *     Array(1..5)   #=> [1, 2, 3, 4, 5]
  *
@@ -179,10 +172,7 @@ 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");
-  }
+  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);
   }
@@ -194,9 +184,9 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
  *  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>.
+ *  Returns a <code>Hash</code> if <i>arg</i> is a <code>Hash</code>.
+ *  Returns an empty <code>Hash</code> when <i>arg</i> is <tt>nil</tt>
+ *  or <tt>[]</tt>.
  *
  *      Hash([])          #=> {}
  *      Hash(nil)         #=> {}
@@ -207,21 +197,13 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
 static mrb_value
 mrb_f_hash(mrb_state *mrb, mrb_value self)
 {
-  mrb_value arg, tmp;
+  mrb_value arg;
 
   mrb_get_args(mrb, "o", &arg);
-  if (mrb_nil_p(arg)) {
+  if (mrb_nil_p(arg) || (mrb_array_p(arg) && RARRAY_LEN(arg) == 0)) {
     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;
+  return mrb_ensure_hash_type(mrb, arg);
 }
 
 /*
index 206b7ac..28f0890 100644 (file)
@@ -49,21 +49,23 @@ assert('Kernel#__method__') do
 end
 
 assert('Kernel#Integer') do
-  assert_equal(123, Integer(123.999)) if class_defined?("Float")
   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) }
+  skip unless Object.const_defined?(:Float)
+  assert_equal(123, Integer(123.999))
 end
 
 assert('Kernel#Float') do
+  skip unless Object.const_defined?(:Float)
   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 if class_defined?("Float")
+end
 
 assert('Kernel#String') do
   assert_equal("main", String(self))
index c182deb..caa16b7 100644 (file)
@@ -23,8 +23,6 @@ domain_error(mrb_state *mrb, const char *func)
 
 #include <float.h>
 
-#define MATH_TOLERANCE 1E-12
-
 double
 asinh(double x)
 {
@@ -122,7 +120,8 @@ erf(double x)
     term *= xsqr/j;
     sum  += term/(2*j+1);
     ++j;
-  } while (fabs(term/sum) > MATH_TOLERANCE);
+    if (sum == 0) break;
+  } while (fabs(term/sum) > DBL_EPSILON);
   return two_sqrtpi*sum;
 }
 
@@ -155,12 +154,16 @@ erfc(double x)
     n += 0.5;
     q1 = q2;
     q2 = b/d;
-  } while (fabs(q1-q2)/q2 > MATH_TOLERANCE);
+  } while (fabs(q1-q2)/q2 > DBL_EPSILON);
   return one_sqrtpi*exp(-x*x)*q2;
 }
 
 #endif
 
+#if defined __FreeBSD__ && !defined __FreeBSD_version
+#include <osreldate.h> /* for __FreeBSD_version */
+#endif
+
 #if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__  &&  __FreeBSD_version < 803000)
 
 double
@@ -738,12 +741,6 @@ mrb_mruby_math_gem_init(mrb_state* mrb)
   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));
index e9ea07c..86a4fc1 100644 (file)
@@ -1,46 +1,31 @@
 ##
 # 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)
+  assert_float(0, Math.sin(0))
 end
 
 assert('Math.sin PI/2') do
-  check_float(Math.sin(Math::PI / 2), 1)
+  assert_float(1, Math.sin(Math::PI / 2))
 end
 
 assert('Math.cos 0') do
-  check_float(Math.cos(0), 1)
+  assert_float(1, Math.cos(0))
 end
 
 assert('Math.cos PI/2') do
-  check_float(Math.cos(Math::PI / 2), 0)
+  assert_float(0, Math.cos(Math::PI / 2))
 end
 
 assert('Math.tan 0') do
-  check_float(Math.tan(0), 0)
+  assert_float(0, Math.tan(0))
 end
 
 assert('Math.tan PI/4') do
-  check_float(Math.tan(Math::PI / 4), 1)
+  assert_float(1, Math.tan(Math::PI / 4))
 end
 
 assert('Fundamental trig identities') do
-  result = true
   N = 13
   N.times do |i|
     a  = Math::PI / N * i
@@ -48,105 +33,100 @@ assert('Fundamental trig identities') do
     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)
+    assert_float(Math.cos(ca), s)
+    assert_float(1 / Math.tan(ca), t)
+    assert_float(1, s ** 2 + c ** 2)
+    assert_float((1/c) ** 2, t ** 2 + 1)
+    assert_float((1/s) ** 2, (1/t) ** 2 + 1)
   end
-  result
 end
 
 assert('Math.erf 0') do
-  check_float(Math.erf(0), 0)
+  assert_float(0, Math.erf(0))
 end
 
 assert('Math.exp 0') do
-  check_float(Math.exp(0), 1.0)
+  assert_float(1.0, Math.exp(0))
 end
 
 assert('Math.exp 1') do
-  check_float(Math.exp(1), 2.718281828459045)
+  assert_float(2.718281828459045, Math.exp(1))
 end
 
 assert('Math.exp 1.5') do
-  check_float(Math.exp(1.5), 4.4816890703380645)
+  assert_float(4.4816890703380645, Math.exp(1.5))
 end
 
 assert('Math.log 1') do
-  check_float(Math.log(1), 0)
+  assert_float(0, Math.log(1))
 end
 
 assert('Math.log E') do
-  check_float(Math.log(Math::E), 1.0)
+  assert_float(1.0, Math.log(Math::E))
 end
 
 assert('Math.log E**3') do
-  check_float(Math.log(Math::E**3), 3.0)
+  assert_float(3.0, Math.log(Math::E**3))
 end
 
 assert('Math.log2 1') do
-  check_float(Math.log2(1), 0.0)
+  assert_float(0.0, Math.log2(1))
 end
 
 assert('Math.log2 2') do
-  check_float(Math.log2(2), 1.0)
+  assert_float(1.0, Math.log2(2))
 end
 
 assert('Math.log10 1') do
-  check_float(Math.log10(1), 0.0)
+  assert_float(0.0, Math.log10(1))
 end
 
 assert('Math.log10 10') do
-  check_float(Math.log10(10), 1.0)
+  assert_float(1.0, Math.log10(10))
 end
 
 assert('Math.log10 10**100') do
-  check_float(Math.log10(10**100), 100.0)
+  assert_float(100.0, Math.log10(10**100))
 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])
+    assert_float(num[i], Math.sqrt(v))
   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])
+    assert_float(num[i], Math.cbrt(v))
   end
-  result
 end
 
 assert('Math.hypot') do
-  check_float(Math.hypot(3, 4), 5.0)
+  assert_float(5.0, Math.hypot(3, 4))
 end
 
 assert('Math.frexp 1234') do
   n = 1234
   fraction, exponent = Math.frexp(n)
-  check_float(Math.ldexp(fraction, exponent), n)
+  assert_float(n, Math.ldexp(fraction, exponent))
 end
 
 assert('Math.erf 1') do
-  check_float(Math.erf(1), 0.842700792949715)
+  assert_float(0.842700792949715, Math.erf(1))
 end
 
 assert('Math.erfc 1') do
-  check_float(Math.erfc(1), 0.157299207050285)
+  assert_float(0.157299207050285, Math.erfc(1))
 end
 
 assert('Math.erf -1') do
-  check_float(Math.erf(-1), -0.8427007929497148)
+  assert_float(-0.8427007929497148, Math.erf(-1))
 end
 
 assert('Math.erfc -1') do
-  check_float(Math.erfc(-1), 1.8427007929497148)
+  assert_float(1.8427007929497148, Math.erfc(-1))
 end
diff --git a/third-party/mruby/mrbgems/mruby-metaprog/mrbgem.rake b/third-party/mruby/mrbgems/mruby-metaprog/mrbgem.rake
new file mode 100644 (file)
index 0000000..1b81d63
--- /dev/null
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-metaprog') do |spec|
+  spec.license = 'MIT'
+  spec.author  = 'mruby developers'
+  spec.summary = 'Meta-programming features for mruby'
+end
diff --git a/third-party/mruby/mrbgems/mruby-metaprog/src/metaprog.c b/third-party/mruby/mrbgems/mruby-metaprog/src/metaprog.c
new file mode 100644 (file)
index 0000000..0aafb4c
--- /dev/null
@@ -0,0 +1,702 @@
+#include "mruby.h"
+#include "mruby/array.h"
+#include "mruby/hash.h"
+#include "mruby/variable.h"
+#include "mruby/proc.h"
+#include "mruby/class.h"
+#include "mruby/string.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;
+
+static mrb_value
+mrb_f_nil(mrb_state *mrb, mrb_value cv)
+{
+  return mrb_nil_value();
+}
+
+/* 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_name_sym_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_name_sym_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_name_sym_check(mrb, iv_name);
+  mrb_iv_set(mrb, self, iv_name, val);
+  return val;
+}
+
+/* 15.3.1.2.7 */
+/*
+ *  call-seq:
+ *     local_variables   -> array
+ *
+ *  Returns the names of local variables in the current scope.
+ *
+ *  [mruby limitation]
+ *  If variable symbol information was stripped out from
+ *  compiled binary files using `mruby-strip -l`, this
+ *  method always returns an empty array.
+ */
+static mrb_value
+mrb_local_variables(mrb_state *mrb, mrb_value self)
+{
+  struct RProc *proc;
+  mrb_irep *irep;
+  mrb_value vars;
+  size_t i;
+
+  proc = mrb->c->ci[-1].proc;
+
+  if (MRB_PROC_CFUNC_P(proc)) {
+    return mrb_ary_new(mrb);
+  }
+  vars = mrb_hash_new(mrb);
+  while (proc) {
+    if (MRB_PROC_CFUNC_P(proc)) break;
+    irep = proc->body.irep;
+    if (!irep->lv) break;
+    for (i = 0; i + 1 < irep->nlocals; ++i) {
+      if (irep->lv[i].name) {
+        mrb_sym sym = irep->lv[i].name;
+        const char *name = mrb_sym2name(mrb, sym);
+        switch (name[0]) {
+        case '*': case '&':
+          break;
+        default:
+          mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value());
+          break;
+        }
+      }
+    }
+    if (!MRB_PROC_ENV_P(proc)) break;
+    proc = proc->upper;
+    //if (MRB_PROC_SCOPE_P(proc)) break;
+    if (!proc->c) break;
+  }
+
+  return mrb_hash_keys(mrb, vars);
+}
+
+KHASH_DECLARE(st, mrb_sym, char, FALSE)
+
+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 || kh_size(h) == 0) return;
+  for (i=0;i<kh_end(h);i++) {
+    if (kh_exist(h, i)) {
+      mrb_method_t m = kh_value(h, i);
+      if (MRB_METHOD_UNDEF_P(m)) continue;
+      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_FL_CLASS_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_capa(mrb, kh_size(set));
+  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.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 */
+}
+
+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;
+}
+
+/* 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_method_t m;
+  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_METHOD_FROM_PROC(m, p);
+  mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, m);
+  return mrb_symbol_value(mid);
+}
+
+static mrb_bool
+cv_name_p(mrb_state *mrb, const char *name, mrb_int len)
+{
+  return len > 2 && name[0] == '@' && name[1] == '@' &&
+         !ISDIGIT(name[2]) && mrb_ident_p(name+2, len-2);
+}
+
+static void
+check_cv_name_sym(mrb_state *mrb, mrb_sym id)
+{
+  mrb_int len;
+  const char *name = mrb_sym2name_len(mrb, id, &len);
+  if (!cv_name_p(mrb, name, len)) {
+    mrb_name_error(mrb, id, "'%S' is not allowed as a class variable name", mrb_sym2str(mrb, id));
+  }
+}
+
+/* 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.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;
+}
+
+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;
+}
+
+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);
+}
+
+static void
+remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
+{
+  struct RClass *c = mrb_class_ptr(mod);
+  khash_t(mt) *h;
+  khiter_t k;
+
+  MRB_CLASS_ORIGIN(c);
+  h = c->mt;
+
+  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, mrb_obj_to_sym(mrb, *argv));
+    argv++;
+  }
+  return mod;
+}
+
+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 */
+}
+
+/* implementation of Module.nesting */
+mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value);
+
+void
+mrb_mruby_metaprog_gem_init(mrb_state* mrb)
+{
+  struct RClass *krn = mrb->kernel_module;
+  struct RClass *mod = mrb->module_class;
+
+  mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.2.4 */
+  mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */
+
+  mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE());
+  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, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */
+  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, "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, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */
+
+  mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */
+  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, "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, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */
+  mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */
+  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_f_nil, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
+  mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */
+  mrb_define_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */
+}
+
+void
+mrb_mruby_metaprog_gem_final(mrb_state* mrb)
+{
+}
diff --git a/third-party/mruby/mrbgems/mruby-metaprog/test/metaprog.rb b/third-party/mruby/mrbgems/mruby-metaprog/test/metaprog.rb
new file mode 100644 (file)
index 0000000..1262c99
--- /dev/null
@@ -0,0 +1,369 @@
+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 String, __send__(:to_s).class
+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(:to_s).class, String
+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])
+  assert_raise(NameError) { o.instance_variable_defined?("@0") }
+end
+
+assert('Kernel#instance_variable_get', '15.3.1.3.21') do
+  o = Class.new { attr_accessor :foo, :bar }.new
+  o.foo = "one"
+  o.bar = 2
+  assert_equal("one", o.instance_variable_get(:@foo))
+  assert_equal(2, o.instance_variable_get("@bar"))
+  assert_equal(nil, o.instance_variable_get(:@baz))
+  %w[foo @1].each do |n|
+    assert_raise(NameError) { o.instance_variable_get(n) }
+  end
+end
+
+assert('Kernel#instance_variable_set', '15.3.1.3.22') do
+  o = Class.new { attr_reader :foo, :_bar }.new
+  assert_equal("one", o.instance_variable_set(:@foo, "one"))
+  assert_equal("one", o.foo)
+  assert_equal(2, o.instance_variable_set("@_bar", 2))
+  assert_equal(2, o._bar)
+  %w[@6 @% @@a @ a].each do |n|
+    assert_raise(NameError) { o.instance_variable_set(n, 1) }
+  end
+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#methods', '15.3.1.3.31') do
+  assert_equal Array, methods.class
+end
+
+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
+
+assert('Kernel#singleton_methods', '15.3.1.3.45') do
+  assert_equal singleton_methods.class, Array
+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: 15.3.1.3.28
+    local_variables.sort
+  }.call(-1, -2)
+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
+
+def labeled_module(name, &block)
+  Module.new do
+    (class <<self; self end).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
+    (class <<self; self end).class_eval do
+      define_method(:to_s) { name }
+      alias_method :inspect, :to_s
+    end
+    class_eval(&block) if block
+  end
+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)
+  assert_raise(NameError) { Test4ClassVariableDefined.class_variable_defined?("@@2") }
+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)
+  assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(:@@a) }
+  %w[@@a? @@! @a a].each do |n|
+    assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(n) }
+  end
+end
+
+assert('Module#class_variable_set', '15.2.2.4.18') do
+  class Test4ClassVariableSet
+    @@foo = 100
+    def foo
+      @@foo
+    end
+  end
+
+  assert_equal 99, Test4ClassVariableSet.class_variable_set(:@@cv, 99)
+  assert_equal 101, 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
+  %w[@@ @@1 @@x= @x @ x 1].each do |n|
+    assert_raise(NameError) { Test4ClassVariableSet.class_variable_set(n, 1) }
+  end
+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#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#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#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#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('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
+  assert_raise(NameError) do
+    Test4RemoveClassVariable.remove_class_variable(:@@cv)
+  end
+  assert_raise(NameError) do
+    Test4RemoveClassVariable.remove_class_variable(:@v)
+  end
+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
+
+  klass = Test4RemoveMethod::Child
+  assert_same klass, klass.class_eval{ remove_method :hello }
+  assert_true klass.instance_methods.include? :hello
+  assert_false klass.instance_methods(false).include? :hello
+end
+
+assert('Module.nesting', '15.2.2.2.2') do
+  module Test4ModuleNesting
+    module Test4ModuleNesting2
+      assert_equal [Test4ModuleNesting2, Test4ModuleNesting],
+                   Module.nesting
+    end
+  end
+  module Test4ModuleNesting::Test4ModuleNesting2
+    assert_equal [Test4ModuleNesting::Test4ModuleNesting2], Module.nesting
+  end
+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('[Bug #7843]') do
+    c.class_eval do
+      remove_method(:foo)
+    end
+  end
+  assert_equal(:foo, removed)
+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("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
+
+assert('alias_method and remove_method') 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
index b2ebd45..eb17df5 100644 (file)
@@ -1,8 +1,9 @@
 module Kernel
   def singleton_method(name)
     m = method(name)
-    if m.owner != singleton_class
-      raise NameError, "undefined method `#{name}' for class `#{singleton_class}'"
+    sc = (class <<self; self; end)
+    if m.owner != sc
+      raise NameError, "undefined method '#{name}' for class '#{sc}'"
     end
     m
   end
index 5de0afd..f7cefa2 100644 (file)
@@ -17,4 +17,12 @@ class Method
   def name
     @name
   end
+
+  def <<(other)
+    ->(*args, &block) { call(other.call(*args, &block)) }
+  end
+
+  def >>(other)
+    ->(*args, &block) { other.call(call(*args, &block)) }
+  end
 end
index e445b34..fa89856 100644 (file)
@@ -327,7 +327,7 @@ name_error:
   s = mrb_class_name(mrb, c);
   mrb_raisef(
     mrb, E_NAME_ERROR,
-    "undefined method `%S' for class `%S'",
+    "undefined method '%S' for class '%S'",
     mrb_sym2str(mrb, name),
     mrb_str_new_static(mrb, s, strlen(s))
   );
@@ -387,7 +387,7 @@ mrb_mruby_method_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, unbound_method, "bind", unbound_method_bind, MRB_ARGS_REQ(1));
   mrb_define_method(mrb, unbound_method, "super_method", method_super_method, MRB_ARGS_NONE());
   mrb_define_method(mrb, unbound_method, "==", method_eql, MRB_ARGS_REQ(1));
-  mrb_alias_method(mrb, unbound_method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+  mrb_define_alias(mrb,  unbound_method, "eql?", "==");
   mrb_define_method(mrb, unbound_method, "to_s", method_to_s, MRB_ARGS_NONE());
   mrb_define_method(mrb, unbound_method, "inspect", method_to_s, MRB_ARGS_NONE());
   mrb_define_method(mrb, unbound_method, "arity", method_arity, MRB_ARGS_NONE());
@@ -396,11 +396,11 @@ mrb_mruby_method_gem_init(mrb_state* mrb)
 
   mrb_undef_class_method(mrb, method, "new");
   mrb_define_method(mrb, method, "==", method_eql, MRB_ARGS_REQ(1));
-  mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+  mrb_define_alias(mrb,  method, "eql?", "==");
   mrb_define_method(mrb, method, "to_s", method_to_s, MRB_ARGS_NONE());
   mrb_define_method(mrb, method, "inspect", method_to_s, MRB_ARGS_NONE());
   mrb_define_method(mrb, method, "call", method_call, MRB_ARGS_ANY());
-  mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "[]"), mrb_intern_lit(mrb, "call"));
+  mrb_define_alias(mrb,  method, "[]", "call");
   mrb_define_method(mrb, method, "unbind", method_unbind, MRB_ARGS_NONE());
   mrb_define_method(mrb, method, "super_method", method_super_method, MRB_ARGS_NONE());
   mrb_define_method(mrb, method, "arity", method_arity, MRB_ARGS_NONE());
index b229a45..dfddde9 100644 (file)
@@ -102,10 +102,7 @@ end
 
 assert 'Method#call for regression' do
   obj = BasicObject.new
-  def obj.foo
-    :ok
-  end
-  assert_equal :ok, Kernel.instance_method(:send).bind(obj).call(:foo), "https://github.com/ksss/mruby-method/issues/4"
+  assert_equal String, Kernel.instance_method(:inspect).bind(obj).call().class, "https://github.com/ksss/mruby-method/issues/4"
 end
 
 assert 'Method#call with undefined method' do
@@ -149,7 +146,7 @@ assert 'Method#source_location' do
   assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location
 
   lineno = __LINE__ + 1
-  klass.define_singleton_method(:s_find_me_if_you_can) {}
+  class <<klass; define_method(:s_find_me_if_you_can) {}; end
   assert_equal [filename, lineno], klass.method(:s_find_me_if_you_can).source_location
 
   klass = Class.new { def respond_to_missing?(m, b); m == :nothing; end }
@@ -243,7 +240,7 @@ assert 'owner' do
 
   assert_equal(c, c.new.method(:foo).owner)
   assert_equal(c, c2.new.method(:foo).owner)
-  assert_equal(c.singleton_class, c2.method(:bar).owner)
+  assert_equal((class <<c; self; end), c2.method(:bar).owner)
 end
 
 assert 'owner missing' do
@@ -374,6 +371,25 @@ assert "Method#initialize_copy" do
   assert_equal(m1, m2)
 end
 
+assert "Method#<< and Method#>>" do
+  obj = Object.new
+  class << obj
+    def mul2(n); n * 2; end
+    def add3(n); n + 3; end
+  end
+
+  f = obj.method(:mul2)
+  g = obj.method(:add3)
+
+  m1 = f << g
+  assert_kind_of Proc, m1
+  assert_equal 16, m1.call(5)
+
+  m2 = f >> g
+  assert_kind_of Proc, m2
+  assert_equal 13, m2.call(5)
+end
+
 assert 'UnboundMethod#arity' do
   c = Class.new {
     def foo(a, b)
@@ -413,12 +429,14 @@ assert 'UnboundMethod#bind' do
   assert_equal(:meth, m.bind(1).call)
   assert_equal(:meth, m.bind(:sym).call)
   assert_equal(:meth, m.bind(Object.new).call)
-  sc = Class.new {
-    class << self
+  sc = nil
+  Class.new {
+    sc = class << self
       def foo
       end
+      self
     end
-  }.singleton_class
+  }
   assert_raise(TypeError) { sc.instance_method(:foo).bind([]) }
   assert_raise(TypeError) { Array.instance_method(:each).bind(1) }
   assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new)
index 1d6a077..cd8bbf1 100644 (file)
@@ -2,13 +2,10 @@
 #include <mruby.h>
 
 static inline mrb_int
-to_int(mrb_value x)
+to_int(mrb_state *mrb, mrb_value x)
 {
-  double f;
-
-  if (mrb_fixnum_p(x)) return mrb_fixnum(x);
-  f = mrb_float(x);
-  return (mrb_int)f;
+  x = mrb_to_int(mrb, x);
+  return mrb_fixnum(x);
 }
 
 /*
@@ -28,7 +25,7 @@ mrb_int_chr(mrb_state *mrb, mrb_value x)
   mrb_int chr;
   char c;
 
-  chr = to_int(x);
+  chr = to_int(mrb, x);
   if (chr >= (1 << CHAR_BIT)) {
     mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
   }
@@ -48,8 +45,8 @@ mrb_int_allbits(mrb_state *mrb, mrb_value self)
 {
   mrb_int n, m;
 
-  n = to_int(self);
   mrb_get_args(mrb, "i", &m);
+  n = to_int(mrb, self);
   return mrb_bool_value((n & m) == m);
 }
 
@@ -64,8 +61,8 @@ mrb_int_anybits(mrb_state *mrb, mrb_value self)
 {
   mrb_int n, m;
 
-  n = to_int(self);
   mrb_get_args(mrb, "i", &m);
+  n = to_int(mrb, self);
   return mrb_bool_value((n & m) != 0);
 }
 
@@ -80,8 +77,8 @@ mrb_int_nobits(mrb_state *mrb, mrb_value self)
 {
   mrb_int n, m;
 
-  n = to_int(self);
   mrb_get_args(mrb, "i", &m);
+  n = to_int(mrb, self);
   return mrb_bool_value((n & m) == 0);
 }
 
index 6ea0c14..c85cb61 100644 (file)
@@ -14,8 +14,9 @@ assert('Integer#div') do
 end
 
 assert('Float#div') do
+  skip unless Object.const_defined?(:Float)
   assert_float 52, 365.2425.div(7)
-end if class_defined?("Float")
+end
 
 assert('Integer#zero?') do
   assert_equal true, 0.zero?
index 7f77363..fbff206 100644 (file)
@@ -3,8 +3,9 @@ assert('NilClass#to_a') do
 end
 
 assert('NilClass#to_f') do
+  skip unless Object.const_defined?(:Float)
   assert_equal 0.0, nil.to_f
-end if class_defined?("Float")
+end
 
 assert('NilClass#to_i') do
   assert_equal 0, nil.to_i
index 3887091..b31dee0 100644 (file)
@@ -57,7 +57,7 @@ os_count_objects(mrb_state *mrb, mrb_value self)
     hash = mrb_hash_new(mrb);
   }
 
-  if (!mrb_test(mrb_hash_empty_p(mrb, hash))) {
+  if (!mrb_hash_empty_p(mrb, hash)) {
     mrb_hash_clear(mrb, hash);
   }
 
index 0553b97..8db89ee 100644 (file)
@@ -1,6 +1,6 @@
 assert('ObjectSpace.count_objects') do
   h = {}
-  f = Fiber.new {} if Object.const_defined? :Fiber
+  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) })
index d96ef10..ac29fdb 100644 (file)
@@ -60,19 +60,39 @@ enum {
 #define PACK_BASE64_IGNORE     0xff
 #define PACK_BASE64_PADDING    0xfe
 
-static int littleendian = 0;
-
 const static unsigned char base64chars[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static signed char base64_dec_tab[128];
+static unsigned char base64_dec_tab[128];
 
+#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER__)
+# define BYTE_ORDER __BYTE_ORDER__
+#endif
+#if !defined(BIG_ENDIAN) && defined(__ORDER_BIG_ENDIAN__)
+# define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+#endif
+#if !defined(LITTLE_ENDIAN) && defined(__ORDER_LITTLE_ENDIAN__)
+# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+#endif
 
-static int
+#ifdef BYTE_ORDER
+# if BYTE_ORDER == BIG_ENDIAN
+#  define littleendian 0
+#  define check_little_endian() (void)0
+# elif BYTE_ORDER == LITTLE_ENDIAN
+#  define littleendian 1
+#  define check_little_endian() (void)0
+# endif
+#endif
+#ifndef littleendian
+/* can't distinguish endian in compile time */
+static int littleendian = 0;
+static void
 check_little_endian(void)
 {
   unsigned int n = 1;
-  return (*(unsigned char *)&n == 1);
+  littleendian = (*(unsigned char *)&n == 1);
 }
+#endif
 
 static unsigned int
 hex2int(unsigned char ch)
@@ -313,21 +333,23 @@ pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned i
   d = mrb_float(o);
 
   if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
-    for (i = 0; i < 8; ++i) {
-      RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+    if (littleendian) {
+      memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
+    }
+    else {
+      for (i = 0; i < 8; ++i) {
+        RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+      }
     }
-#else
-    memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
-#endif
   } else {
-#ifdef MRB_ENDIAN_BIG
-    memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
-#else
-    for (i = 0; i < 8; ++i) {
-      RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+    if (littleendian) {
+      for (i = 0; i < 8; ++i) {
+        RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+      }
+    }
+    else {
+      memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
     }
-#endif
   }
 
   return 8;
@@ -341,21 +363,23 @@ unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value a
   uint8_t *buffer = (uint8_t *)&d;
 
   if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
-    for (i = 0; i < 8; ++i) {
-      buffer[8 - i - 1] = src[i];
+    if (littleendian) {
+      memcpy(buffer, src, 8);
+    }
+    else {
+      for (i = 0; i < 8; ++i) {
+        buffer[8 - i - 1] = src[i];
+      }
     }
-#else
-    memcpy(buffer, src, 8);
-#endif
   } else {
-#ifdef MRB_ENDIAN_BIG
-    memcpy(buffer, src, 8);
-#else
-    for (i = 0; i < 8; ++i) {
-      buffer[8 - i - 1] = src[i];
+    if (littleendian) {
+      for (i = 0; i < 8; ++i) {
+        buffer[8 - i - 1] = src[i];
+      }
+    }
+    else {
+      memcpy(buffer, src, 8);
     }
-#endif
   }
   mrb_ary_push(mrb, ary, mrb_float_value(mrb, d));
 
@@ -372,21 +396,23 @@ pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned in
   f = (float)mrb_float(o);
 
   if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
-    for (i = 0; i < 4; ++i) {
-      RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+    if (littleendian) {
+      memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
+    }
+    else {
+      for (i = 0; i < 4; ++i) {
+        RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+      }
     }
-#else
-    memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
-#endif
   } else {
-#ifdef MRB_ENDIAN_BIG
-    memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
-#else
-    for (i = 0; i < 4; ++i) {
-      RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+    if (littleendian) {
+      for (i = 0; i < 4; ++i) {
+        RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+      }
+    }
+    else {
+      memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
     }
-#endif
   }
 
   return 4;
@@ -400,21 +426,23 @@ unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ar
   uint8_t *buffer = (uint8_t *)&f;
 
   if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
-    for (i = 0; i < 4; ++i) {
-      buffer[4 - i - 1] = src[i];
+    if (littleendian) {
+      memcpy(buffer, src, 4);
+    }
+    else {
+      for (i = 0; i < 4; ++i) {
+        buffer[4 - i - 1] = src[i];
+      }
     }
-#else
-    memcpy(buffer, src, 4);
-#endif
   } else {
-#ifdef MRB_ENDIAN_BIG
-    memcpy(buffer, src, 4);
-#else
-    for (i = 0; i < 4; ++i) {
-      buffer[4 - i - 1] = src[i];
+    if (littleendian) {
+      for (i = 0; i < 4; ++i) {
+        buffer[4 - i - 1] = src[i];
+      }
+    }
+    else {
+      memcpy(buffer, src, 4);
     }
-#endif
   }
   mrb_ary_push(mrb, ary, mrb_float_value(mrb, f));
 
@@ -429,11 +457,6 @@ pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count,
   int len = 0;
   uint32_t c = 0;
 
-#ifndef MRB_WITHOUT_FLOAT
-  if (mrb_float_p(o)) {
-    goto range_error;
-  }
-#endif
   c = (uint32_t)mrb_fixnum(o);
 
   /* Unicode character */
@@ -461,9 +484,6 @@ pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count,
     len = 4;
   }
   else {
-#ifndef MRB_WITHOUT_FLOAT
-range_error:
-#endif
     mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range");
   }
 
@@ -510,7 +530,7 @@ utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
   }
   if (n > *lenp) {
     mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)",
-    mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
+               mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
   }
   *lenp = n--;
   if (n != 0) {
@@ -598,19 +618,21 @@ unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, u
   }
   copylen = slen;
 
-  if (flags & PACK_FLAG_Z) {  /* "Z" */
+  if (slen >= 0 && flags & PACK_FLAG_Z) {  /* "Z" */
     if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) {
       copylen = (int)(cp - sptr);
       if (count == -1) {
         slen = copylen + 1;
       }
     }
-  } else if (!(flags & PACK_FLAG_a)) {  /* "A" */
-    while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
+  }
+  else if (!(flags & PACK_FLAG_a)) {  /* "A" */
+    while (copylen > 0 && (sptr[copylen - 1] == '\0' || ISSPACE(sptr[copylen - 1]))) {
       copylen--;
     }
   }
 
+  if (copylen < 0) copylen = 0;
   dst = mrb_str_new(mrb, sptr, (mrb_int)copylen);
   mrb_ary_push(mrb, ary, dst);
   return slen;
@@ -1050,12 +1072,12 @@ alias:
   /* read suffix [0-9*_!<>] */
   while (tmpl->idx < tlen) {
     ch = tptr[tmpl->idx++];
-    if (isdigit(ch)) {
+    if (ISDIGIT(ch)) {
       count = ch - '0';
-      while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) {
+      while (tmpl->idx < tlen && ISDIGIT(tptr[tmpl->idx])) {
         count = count * 10 + (tptr[tmpl->idx++] - '0');
         if (count < 0) {
-          mrb_raisef(mrb, E_RUNTIME_ERROR, "too big template length");
+          mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length");
         }
       }
       continue;  /* special case */
@@ -1122,13 +1144,16 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
       o = mrb_ary_ref(mrb, ary, aidx);
       if (type == PACK_TYPE_INTEGER) {
         o = mrb_to_int(mrb, o);
+      }
 #ifndef MRB_WITHOUT_FLOAT
-      else if (type == PACK_TYPE_FLOAT) {
+      else if (type == PACK_TYPE_FLOAT) {
         if (!mrb_float_p(o)) {
-          o = mrb_funcall(mrb, o, "to_f", 0);
+          mrb_float f = mrb_to_flo(mrb, o);
+          o = mrb_float_value(mrb, f);
         }
+      }
 #endif
-      else if (type == PACK_TYPE_STRING) {
+      else if (type == PACK_TYPE_STRING) {
         if (!mrb_string_p(o)) {
           mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
         }
@@ -1188,7 +1213,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
 }
 
 static mrb_value
-mrb_pack_unpack(mrb_state *mrb, mrb_value str)
+pack_unpack(mrb_state *mrb, mrb_value str, int single)
 {
   mrb_value result;
   struct tmpl tmpl;
@@ -1270,19 +1295,34 @@ mrb_pack_unpack(mrb_state *mrb, mrb_value str)
         count--;
       }
     }
+    if (single) break;
   }
 
+  if (single) return RARRAY_PTR(result)[0];
   return result;
 }
 
+static mrb_value
+mrb_pack_unpack(mrb_state *mrb, mrb_value str)
+{
+  return pack_unpack(mrb, str, 0);
+}
+
+static mrb_value
+mrb_pack_unpack1(mrb_state *mrb, mrb_value str)
+{
+  return pack_unpack(mrb, str, 1);
+}
+
 void
 mrb_mruby_pack_gem_init(mrb_state *mrb)
 {
-  littleendian = check_little_endian();
+  check_little_endian();
   make_base64_dec_tab();
 
   mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1));
   mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, mrb->string_class, "unpack1", mrb_pack_unpack1, MRB_ARGS_REQ(1));
 }
 
 void
index f518ca4..1788c2b 100644 (file)
@@ -129,7 +129,7 @@ assert 'pack/unpack "i"' do
   if PACK_IS_LITTLE_ENDIAN
     str = "\xC7\xCF" + "\xFF" * (int_size-2)
   else
-    str = "\xFF" * (int_size-2) + "\xC7\xCF"
+    str = "\xFF" * (int_size-2) + "\xCF\xC7"
   end
   assert_pack 'i', str, [-12345]
 end
@@ -141,7 +141,7 @@ assert 'pack/unpack "I"' do
   if PACK_IS_LITTLE_ENDIAN
     str = "\x39\x30" + "\0" * (uint_size-2)
   else
-    str = "\0" * (uint_size-2) + "\x39\x30"
+    str = "\0" * (uint_size-2) + "\x30\x39"
   end
   assert_pack 'I', str, [12345]
 end
index 38a1066..27567d8 100644 (file)
@@ -45,16 +45,13 @@ module Kernel
       __printstr__ "\n"
       i += 1
     end
-    args[0]
+    args.__svalue
   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))
index e181b06..f7f99fc 100644 (file)
@@ -23,7 +23,6 @@ printstr(mrb_state *mrb, mrb_value 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),
index b716639..abe9c79 100644 (file)
@@ -27,7 +27,7 @@ class Proc
 
     pproc = self
     make_curry = proc do |given_args=[]|
-      send(type) do |*args|
+      __send__(type) do |*args|
         new_args = given_args + args
         if new_args.size >= arity
           pproc[*new_args]
@@ -39,4 +39,12 @@ class Proc
     make_curry.call
   end
 
+  def <<(other)
+    ->(*args, &block) { call(other.call(*args, &block)) }
+  end
+
+  def >>(other)
+    ->(*args, &block) { other.call(call(*args, &block)) }
+  end
+
 end
index b13606f..17884e3 100644 (file)
@@ -25,8 +25,8 @@ mrb_proc_source_location(mrb_state *mrb, mrb_value self)
     int32_t line;
     const char *filename;
 
-    filename = mrb_debug_get_filename(irep, 0);
-    line = mrb_debug_get_line(irep, 0);
+    filename = mrb_debug_get_filename(mrb, irep, 0);
+    line = mrb_debug_get_line(mrb, irep, 0);
 
     return (!filename && line == -1)? mrb_nil_value()
         : mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line));
@@ -46,11 +46,11 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self)
     int32_t line;
     mrb_str_cat_lit(mrb, str, "@");
 
-    filename = mrb_debug_get_filename(irep, 0);
+    filename = mrb_debug_get_filename(mrb, irep, 0);
     mrb_str_cat_cstr(mrb, str, filename ? filename : "-");
     mrb_str_cat_lit(mrb, str, ":");
 
-    line = mrb_debug_get_line(irep, 0);
+    line = mrb_debug_get_line(mrb, irep, 0);
     if (line != -1) {
       str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line));
     }
@@ -94,21 +94,23 @@ static mrb_value
 mrb_proc_parameters(mrb_state *mrb, mrb_value self)
 {
   struct parameters_type {
-    int size;
+    size_t len;
     const char *name;
+    int size;
   } *p, parameters_list [] = {
-    {0, "req"},
-    {0, "opt"},
-    {0, "rest"},
-    {0, "req"},
-    {0, "block"},
-    {0, NULL}
+    {sizeof("req")   - 1, "req",   0},
+    {sizeof("opt")   - 1, "opt",   0},
+    {sizeof("rest")  - 1, "rest",  0},
+    {sizeof("req")   - 1, "req",   0},
+    {sizeof("block") - 1, "block", 0},
+    {0, NULL, 0}
   };
   const struct RProc *proc = mrb_proc_ptr(self);
   const struct mrb_irep *irep = proc->body.irep;
   mrb_aspec aspec;
-  mrb_value sname, parameters;
+  mrb_value parameters;
   int i, j;
+  int max = -1;
 
   if (MRB_PROC_CFUNC_P(proc)) {
     // TODO cfunc aspec is not implemented yet
@@ -120,16 +122,18 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
   if (!irep->lv) {
     return mrb_ary_new(mrb);
   }
-  if (GET_OPCODE(*irep->iseq) != OP_ENTER) {
+  if (*irep->iseq != OP_ENTER) {
     return mrb_ary_new(mrb);
   }
 
   if (!MRB_PROC_STRICT_P(proc)) {
+    parameters_list[0].len = sizeof("opt") - 1;
     parameters_list[0].name = "opt";
+    parameters_list[3].len = sizeof("opt") - 1;
     parameters_list[3].name = "opt";
   }
 
-  aspec = GETARG_Ax(*irep->iseq);
+  aspec = PEEK_W(irep->iseq+1);
   parameters_list[0].size = MRB_ASPEC_REQ(aspec);
   parameters_list[1].size = MRB_ASPEC_OPT(aspec);
   parameters_list[2].size = MRB_ASPEC_REST(aspec);
@@ -138,14 +142,25 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
 
   parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
 
+  max = 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));
+    mrb_value sname = mrb_symbol_value(mrb_intern_static(mrb, p->name, p->len));
+
     for (j = 0; j < p->size; i++, j++) {
-      mrb_value a = mrb_ary_new(mrb);
+      mrb_value a;
+
+      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));
+      if (i < max && irep->lv[i].name) {
+        mrb_sym sym = irep->lv[i].name;
+        const char *name = mrb_sym2name(mrb, sym);
+        switch (name[0]) {
+        case '*': case '&':
+          break;
+        default:
+          mrb_ary_push(mrb, a, mrb_symbol_value(sym));
+          break;
+        }
       }
       mrb_ary_push(mrb, parameters, a);
     }
index 037d8d1..1220841 100644 (file)
@@ -13,6 +13,11 @@ assert('Proc#inspect') do
   assert_kind_of String, ins
 end
 
+assert('Proc#parameters') do
+  parameters = Proc.new{|x,y=42,*other|}.parameters
+  assert_equal [[:opt, :x], [:opt, :y], [:rest, :other]], parameters
+end
+
 assert('Proc#lambda?') do
   assert_true lambda{}.lambda?
   assert_true !Proc.new{}.lambda?
@@ -72,6 +77,19 @@ assert('Kernel#proc') do
   end
 end
 
+assert "Proc#<< and Proc#>>" do
+  add3 = ->(n) { n + 3 }
+  mul2 = ->(n) { n * 2 }
+
+  f1 = mul2 << add3
+  assert_kind_of Proc, f1
+  assert_equal 16, f1.call(5)
+
+  f2 = mul2 >> add3
+  assert_kind_of Proc, f2
+  assert_equal 13, f2.call(5)
+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)
index b865244..6820984 100644 (file)
@@ -79,12 +79,12 @@ get_opt(mrb_state* mrb)
   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));
+    mrb_int i;
+
+    arg = mrb_to_int(mrb, arg);
+    i = mrb_fixnum(arg);
+    if (i < 0) {
+      arg = mrb_fixnum_value(0 - i);
     }
   }
   return arg;
@@ -219,7 +219,7 @@ mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
       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))));
 
index 1c59be3..1653ae4 100644 (file)
@@ -74,15 +74,3 @@ assert('Array#shuffle!(random)') do
 
   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 e5d1fb0..de7925b 100644 (file)
@@ -15,10 +15,7 @@ class Range
 
     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)
+    n = nv.__to_int
     raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
     ary = []
     each do |i|
index aca71cc..1f66909 100644 (file)
@@ -40,7 +40,7 @@ r_lt(mrb_state *mrb, mrb_value a, mrb_value b)
  *     ("a".."z").cover?("cc")   #=> true
  */
 static mrb_value
-mrb_range_cover(mrb_state *mrb, mrb_value range)
+range_cover(mrb_state *mrb, mrb_value range)
 {
   mrb_value val;
   struct RRange *r = mrb_range_ptr(mrb, range);
@@ -48,11 +48,11 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
 
   mrb_get_args(mrb, "o", &val);
 
-  beg = r->edges->beg;
-  end = r->edges->end;
+  beg = RANGE_BEG(r);
+  end = RANGE_END(r);
 
   if (r_le(mrb, beg, val)) {
-    if (r->excl) {
+    if (RANGE_EXCL(r)) {
       if (r_lt(mrb, val, end))
         return mrb_true_value();
     }
@@ -82,14 +82,13 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
  *    (10...20).last(3)  #=> [17, 18, 19]
  */
 static mrb_value
-mrb_range_last(mrb_state *mrb, mrb_value range)
+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;
+    return mrb_range_end(mrb, range);
   }
 
   array = mrb_funcall(mrb, range, "to_a", 0);
@@ -108,7 +107,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
  */
 
 static mrb_value
-mrb_range_size(mrb_state *mrb, mrb_value range)
+range_size(mrb_state *mrb, mrb_value range)
 {
   struct RRange *r = mrb_range_ptr(mrb, range);
   mrb_value beg, end;
@@ -116,9 +115,9 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
   mrb_bool num_p = TRUE;
   mrb_bool excl;
 
-  beg = r->edges->beg;
-  end = r->edges->end;
-  excl = r->excl;
+  beg = RANGE_BEG(r);
+  end = RANGE_END(r);
+  excl = RANGE_EXCL(r);
   if (mrb_fixnum_p(beg)) {
     beg_f = (mrb_float)mrb_fixnum(beg);
   }
@@ -165,9 +164,9 @@ 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());
+  mrb_define_method(mrb, s, "cover?", range_cover, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "last",   range_last,  MRB_ARGS_OPT(1));
+  mrb_define_method(mrb, s, "size",   range_size,  MRB_ARGS_NONE());
 }
 
 void
diff --git a/third-party/mruby/mrbgems/mruby-sleep/.gitignore b/third-party/mruby/mrbgems/mruby-sleep/.gitignore
new file mode 100644 (file)
index 0000000..b9f9e71
--- /dev/null
@@ -0,0 +1,4 @@
+/mruby
+
+*.so
+*.a
\ No newline at end of file
diff --git a/third-party/mruby/mrbgems/mruby-sleep/.travis.yml b/third-party/mruby/mrbgems/mruby-sleep/.travis.yml
new file mode 100644 (file)
index 0000000..ad6b007
--- /dev/null
@@ -0,0 +1,29 @@
+dist: trusty
+
+language: c
+compiler:
+  - gcc
+  - clang
+env:
+  - MRUBY_VERSION=1.2.0
+  - MRUBY_VERSION=master
+matrix:
+  allow_failures:
+    - env: MRUBY_VERSION=master
+branches:
+  only:
+    - master
+addons:
+  apt:
+    packages:
+      - rake
+      - bison
+      - git
+      - gperf
+      # - aclocal
+      # - automake
+      # - autoconf
+      # - autotools-dev
+
+script:
+  - rake test
\ No newline at end of file
diff --git a/third-party/mruby/mrbgems/mruby-sleep/.travis_build_config.rb b/third-party/mruby/mrbgems/mruby-sleep/.travis_build_config.rb
new file mode 100644 (file)
index 0000000..b32e38a
--- /dev/null
@@ -0,0 +1,6 @@
+MRuby::Build.new do |conf|
+  toolchain :gcc
+  conf.gembox 'default'
+  conf.gem '../mruby-sleep'
+  conf.enable_test
+end
diff --git a/third-party/mruby/mrbgems/mruby-sleep/README.md b/third-party/mruby/mrbgems/mruby-sleep/README.md
new file mode 100644 (file)
index 0000000..7707cd0
--- /dev/null
@@ -0,0 +1,27 @@
+# Sleep Module for mruby
+mruby sleep module
+
+## install by mrbgems
+ - add conf.gem line to `build_config.rb`
+```ruby
+MRuby::Build.new do |conf|
+
+    # ... (snip) ...
+
+    conf.gem :core => 'mruby-sleep'
+end
+```
+
+## example
+
+```ruby
+sleep(10)
+usleep(10000)
+```
+
+# License
+under the MIT License:
+
+* http://www.opensource.org/licenses/mit-license.php
+
+
diff --git a/third-party/mruby/mrbgems/mruby-sleep/Rakefile b/third-party/mruby/mrbgems/mruby-sleep/Rakefile
new file mode 100644 (file)
index 0000000..5e3c461
--- /dev/null
@@ -0,0 +1,29 @@
+MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || ".travis_build_config.rb")
+MRUBY_VERSION=ENV["MRUBY_VERSION"] || "1.2.0"
+
+file :mruby do
+  cmd =  "git clone --depth=1 git://github.com/mruby/mruby.git"
+  if MRUBY_VERSION != 'master'
+    cmd << " && cd mruby"
+    cmd << " && git fetch --tags && git checkout $(git rev-parse #{MRUBY_VERSION})"
+  end
+  sh cmd
+end
+
+desc "compile binary"
+task :compile => :mruby do
+  sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all"
+end
+
+desc "test"
+task :test => :mruby do
+  sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all test"
+end
+
+desc "cleanup"
+task :clean do
+  exit 0 unless File.directory?('mruby')
+  sh "cd mruby && rake deep_clean"
+end
+
+task :default => :compile
diff --git a/third-party/mruby/mrbgems/mruby-sleep/example/sleep.rb b/third-party/mruby/mrbgems/mruby-sleep/example/sleep.rb
new file mode 100644 (file)
index 0000000..e5acea3
--- /dev/null
@@ -0,0 +1,3 @@
+sleep(10)
+usleep(10000)
+
diff --git a/third-party/mruby/mrbgems/mruby-sleep/mrbgem.rake b/third-party/mruby/mrbgems/mruby-sleep/mrbgem.rake
new file mode 100644 (file)
index 0000000..8827b35
--- /dev/null
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-sleep') do |spec|
+  spec.license = 'MIT'
+  spec.authors = 'MATSUMOTO Ryosuke'
+  spec.version = '0.0.1'
+end
diff --git a/third-party/mruby/mrbgems/mruby-sleep/src/mrb_sleep.c b/third-party/mruby/mrbgems/mruby-sleep/src/mrb_sleep.c
new file mode 100644 (file)
index 0000000..3f8ef90
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+** mrb_sleep - sleep methods for mruby
+**
+** Copyright (c) mod_mruby developers 2012-
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 ]
+*/
+
+#include <time.h>
+#ifdef _WIN32
+    #include <windows.h>
+    #define sleep(x) Sleep(x * 1000)
+    #define usleep(x) Sleep((DWORD)((x)<1000) ? 1 : ((x)/1000))
+#else
+    #include <unistd.h>
+    #include <sys/time.h>
+#endif
+
+#include "mruby.h"
+
+/* not implemented forever sleep (called without an argument)*/
+static mrb_value
+mrb_f_sleep(mrb_state *mrb, mrb_value self)
+{
+    time_t beg = time(0);
+    time_t end;
+#ifndef MRB_WITHOUT_FLOAT
+    mrb_float sec;
+
+    mrb_get_args(mrb, "f", &sec);
+    if (sec >= 0) {
+        usleep(sec * 1000000);
+    }
+    else {
+        mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
+    }
+#else
+    mrb_int sec;
+
+    mrb_get_args(mrb, "i", &sec);
+    if (sec >= 0) {
+        sleep(sec);
+    } else {
+        mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
+    }
+#endif
+    end = time(0) - beg;
+
+    return mrb_fixnum_value(end);
+}
+
+/* mruby special; needed for mruby without float numbers */
+static mrb_value
+mrb_f_usleep(mrb_state *mrb, mrb_value self)
+{
+    mrb_int usec;
+#ifdef _WIN32
+    FILETIME st_ft,ed_ft;
+    unsigned __int64 st_time = 0;
+    unsigned __int64 ed_time = 0;
+#else
+    struct timeval st_tm,ed_tm;
+#endif
+    time_t slp_tm;
+
+#ifdef _WIN32
+    GetSystemTimeAsFileTime(&st_ft);
+#else
+    gettimeofday(&st_tm, NULL);
+#endif
+
+    /* not implemented forever sleep (called without an argument)*/
+    mrb_get_args(mrb, "i", &usec);
+
+    if (usec >= 0) {
+        usleep(usec);
+    } else {
+        mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative integer");
+    }
+
+#ifdef _WIN32
+    GetSystemTimeAsFileTime(&ed_ft);
+
+    st_time |= st_ft.dwHighDateTime;
+    st_time <<=32;
+    st_time |= st_ft.dwLowDateTime;
+    ed_time |= ed_ft.dwHighDateTime;
+    ed_time <<=32;
+    ed_time |= ed_ft.dwLowDateTime;
+
+    slp_tm = (ed_time - st_time) / 10;
+#else
+    gettimeofday(&ed_tm, NULL);
+
+    if (st_tm.tv_usec > ed_tm.tv_usec) {
+        slp_tm = 1000000 + ed_tm.tv_usec - st_tm.tv_usec;
+    }
+    else {
+        slp_tm = ed_tm.tv_usec - st_tm.tv_usec;
+    }
+#endif
+
+    return mrb_fixnum_value(slp_tm);
+}
+
+void
+mrb_mruby_sleep_gem_init(mrb_state *mrb)
+{
+    mrb_define_method(mrb, mrb->kernel_module, "sleep",   mrb_f_sleep,   MRB_ARGS_REQ(1));
+    mrb_define_method(mrb, mrb->kernel_module, "usleep",  mrb_f_usleep,  MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_sleep_gem_final(mrb_state *mrb)
+{
+}
diff --git a/third-party/mruby/mrbgems/mruby-sleep/test/sleep_test.rb b/third-party/mruby/mrbgems/mruby-sleep/test/sleep_test.rb
new file mode 100644 (file)
index 0000000..f05b7a3
--- /dev/null
@@ -0,0 +1,29 @@
+assert("sleep works") do
+  assert_nothing_raised { sleep(1) }
+  assert_nothing_raised { sleep(0) }
+end
+
+assert("sleep would accept non-negative float value") do
+  skip unless Object.const_defined?(:Float)
+  assert_nothing_raised { sleep(0.01) }
+  assert_nothing_raised { sleep(0.0) }
+  assert_nothing_raised { sleep(-0.0) }
+end
+
+assert("sleep would not accept negative integer value") do
+  assert_raise(ArgumentError) { sleep(-1) }
+end
+
+assert("sleep would not accept negative float value") do
+  skip unless Object.const_defined?(:Float)
+  assert_raise(ArgumentError) { sleep(-0.1) }
+end
+
+assert("usleep works") do
+  assert_nothing_raised { usleep(100) }
+  assert_nothing_raised { usleep(0) }
+end
+
+assert("usleep would not accept negative value") do
+  assert_raise(ArgumentError) { usleep(-100) }
+end
index 8096815..b0894e0 100644 (file)
@@ -4,6 +4,7 @@ MRuby::Gem::Specification.new('mruby-socket') do |spec|
   spec.summary = 'standard socket class'
 
   spec.cc.include_paths << "#{build.root}/src"
+  #spec.cc.defines << "HAVE_SA_LEN=0"
 
   # If Windows, use winsock
   if ( /mswin|mingw|win32/ =~ RUBY_PLATFORM ) then
index 4d56d53..dff1767 100644 (file)
@@ -10,6 +10,7 @@
   #include <winsock2.h>
   #include <ws2tcpip.h>
   #include <windows.h>
+  #include <winerror.h>
 
   #define SHUT_RDWR SD_BOTH
   #ifndef _SSIZE_T_DEFINED
@@ -19,6 +20,7 @@
 #else
   #include <sys/types.h>
   #include <sys/socket.h>
+  #include <sys/param.h>
   #include <sys/un.h>
   #include <netinet/in.h>
   #include <netinet/tcp.h>
 
 #include "mruby/ext/io.h"
 
+#if !defined(HAVE_SA_LEN)
+#if (defined(BSD) && (BSD >= 199006))
+#define HAVE_SA_LEN  1
+#else
+#define HAVE_SA_LEN  0
+#endif
+#endif
+
 #define E_SOCKET_ERROR             (mrb_class_get(mrb, "SocketError"))
 
 #if !defined(mrb_cptr)
@@ -51,7 +61,7 @@
 #endif
 
 #ifdef _WIN32
-const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
+static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
 {
     if (af == AF_INET)
     {
@@ -76,7 +86,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
     return NULL;
 }
 
-int inet_pton(int af, const char *src, void *dst)
+static int inet_pton(int af, const char *src, void *dst)
 {
     struct addrinfo hints, *res, *ressave;
 
@@ -194,8 +204,8 @@ mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self)
     mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr");
   }
   error = getnameinfo((struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, (int)flags);
-  if (error != 0) {
-    mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error));
+  if (error) {
+    mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error)));
   }
   ary = mrb_ary_new_capa(mrb, 2);
   mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
@@ -214,7 +224,11 @@ mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self)
   sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
   if (((struct sockaddr *)RSTRING_PTR(sastr))->sa_family != AF_UNIX)
     mrb_raise(mrb, E_SOCKET_ERROR, "need AF_UNIX address");
-  return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
+  if (RSTRING_LEN(sastr) < (mrb_int)offsetof(struct sockaddr_un, sun_path) + 1) {
+    return mrb_str_new(mrb, "", 0);
+  } else {
+    return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
+  }
 }
 #endif
 
@@ -287,7 +301,7 @@ mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self)
   mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)egid));
   return ary;
 #else
-  mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not avaialble on this system");
+  mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not available on this system");
   return mrb_nil_value();
 #endif
 }
@@ -456,12 +470,12 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
     }
   } else if (argc == 1) {
     if (strcmp(mrb_obj_classname(mrb, so), "Socket::Option") != 0)
-      mrb_raisef(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
+      mrb_raise(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
     level = mrb_fixnum(mrb_funcall(mrb, so, "level", 0));
     optname = mrb_fixnum(mrb_funcall(mrb, so, "optname", 0));
     optval = mrb_funcall(mrb, so, "data", 0);
   } else {
-    mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%d for 3)", argc);
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 3)", mrb_fixnum_value(argc));
   }
 
   s = socket_fd(mrb, self);
@@ -664,19 +678,15 @@ mrb_socket_listen(mrb_state *mrb, mrb_value klass)
 static mrb_value
 mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass)
 {
-  mrb_value sa;
+  const struct sockaddr *sa;
+  mrb_value str;
 
-  mrb_get_args(mrb, "S", &sa);
-#ifdef __linux__
-  if ((size_t)RSTRING_LEN(sa) < offsetof(struct sockaddr, sa_family) + sizeof(sa_family_t)) {
-    mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
-  }
-#else
-  if ((size_t)RSTRING_LEN(sa) < sizeof(struct sockaddr)) {
-    mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
+  mrb_get_args(mrb, "S", &str);
+  if ((size_t)RSTRING_LEN(str) < offsetof(struct sockaddr, sa_family) + sizeof(sa->sa_family)) {
+    mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
   }
-#endif
-  return mrb_fixnum_value(((struct sockaddr *)RSTRING_PTR(sa))->sa_family);
+  sa = (const struct sockaddr *)RSTRING_PTR(str);
+  return mrb_fixnum_value(sa->sa_family);
 }
 
 static mrb_value
@@ -691,10 +701,13 @@ mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass)
 
   mrb_get_args(mrb, "S", &path);
   if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) {
-    mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %ubytes)", (unsigned int)sizeof(sunp->sun_path) - 1);
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %S bytes)", mrb_fixnum_value(sizeof(sunp->sun_path) - 1));
   }
   s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un));
   sunp = (struct sockaddr_un *)RSTRING_PTR(s);
+#if HAVE_SA_LEN
+  sunp->sun_len = sizeof(struct sockaddr_un);
+#endif
   sunp->sun_family = AF_UNIX;
   memcpy(sunp->sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
   sunp->sun_path[RSTRING_LEN(path)] = '\0';
index 3ae46cd..4916561 100644 (file)
@@ -7,7 +7,7 @@ assert('super class of Addrinfo') do
 end
 
 assert('Addrinfo.getaddrinfo') do
-  ary = Addrinfo.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_STREAM)
+  ary = Addrinfo.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_STREAM)
   assert_true(ary.size >= 1)
   ai = ary[0]
   assert_equal(ai.afamily, Socket::AF_INET)
@@ -19,9 +19,9 @@ end
 
 assert('Addrinfo.foreach') do
   # assume Addrinfo.getaddrinfo works well
-  a = Addrinfo.getaddrinfo("localhost", "domain")
+  a = Addrinfo.getaddrinfo("localhost", 80)
   b = []
-  Addrinfo.foreach("localhost", "domain") { |ai| b << ai }
+  Addrinfo.foreach("localhost", 80) { |ai| b << ai }
   assert_equal(a.size, b.size)
 end
 
@@ -35,7 +35,7 @@ assert('Addrinfo.ip') do
 end
 
 assert('Addrinfo.tcp') do
-  ai = Addrinfo.tcp('127.0.0.1', 'smtp')
+  ai = Addrinfo.tcp('127.0.0.1', 25)
   assert_equal('127.0.0.1', ai.ip_address)
   assert_equal(Socket::AF_INET, ai.afamily)
   assert_equal(25, ai.ip_port)
@@ -44,7 +44,7 @@ assert('Addrinfo.tcp') do
 end
 
 assert('Addrinfo.udp') do
-  ai = Addrinfo.udp('127.0.0.1', 'domain')
+  ai = Addrinfo.udp('127.0.0.1', 53)
   assert_equal('127.0.0.1', ai.ip_address)
   assert_equal(Socket::AF_INET, ai.afamily)
   assert_equal(53, ai.ip_port)
index aa89358..b64a679 100644 (file)
@@ -5,7 +5,7 @@ assert('Socket.gethostname') do
 end
 
 assert('Socket::getaddrinfo') do
-  ret = Socket.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_DGRAM)
+  ret = Socket.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_DGRAM)
   assert_true ret.size >= 1
   a = ret[0]
   assert_equal "AF_INET",           a[0]
@@ -28,7 +28,7 @@ assert('Socket#recvfrom') do
     rstr, ai = s.recvfrom sstr.size
 
     assert_equal sstr, rstr
-    assert_true "127.0.0.1", ai.ip_address
+    assert_equal "127.0.0.1", ai.ip_address
   ensure
     s.close rescue nil
     c.close rescue nil
index 086bc48..3017c7c 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <fcntl.h>
 #include <sys/stat.h>
+#define open  _open
 #define close _close
 #define unlink _unlink
 
index 7eea1a1..985ffe2 100644 (file)
@@ -119,13 +119,11 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
 #define FPREC0 128
 
 #define CHECK(l) do {\
-/*  int cr = ENC_CODERANGE(result);*/\
   while ((l) >= bsiz - blen) {\
+    if (bsiz > MRB_INT_MAX/2) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
     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)
 
@@ -202,11 +200,10 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
 
 #define GETNUM(n, val) \
   for (; p < end && ISDIGIT(*p); p++) {\
-    mrb_int next_n = 10 * n + (*p - '0'); \
-    if (next_n / 10 != n) {\
+    if (n > (MRB_INT_MAX - (*p - '0'))/10) {\
       mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \
     } \
-    n = next_n; \
+    n = 10 * n + (*p - '0'); \
   } \
   if (p >= end) { \
     mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \
@@ -236,7 +233,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv)
   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");
+  tmp = mrb_check_hash_type(mrb, argv[1]);
   if (mrb_nil_p(tmp)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
   }
@@ -555,7 +552,7 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
 
   ++argc;
   --argv;
-  fmt = mrb_str_to_str(mrb, fmt);
+  mrb_to_str(mrb, fmt);
   p = RSTRING_PTR(fmt);
   end = p + RSTRING_LEN(fmt);
   blen = 0;
@@ -1059,18 +1056,22 @@ retry:
           if (i > 0)
             need = BIT_DIGITS(i);
         }
+        if (need > MRB_INT_MAX - ((flags&FPREC) ? prec : 6)) {
+        too_big_width:
+          mrb_raise(mrb, E_ARGUMENT_ERROR,
+                    (width > prec ? "width too big" : "prec too big"));
+        }
         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"));
+        if (need > MRB_INT_MAX - 20) {
+          goto too_big_width;
         }
+        need += 20;
 
         CHECK(need);
         n = snprintf(&buf[blen], need, fbuf, fval);
-        if (n < 0) {
+        if (n < 0 || n >= need) {
           mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
         }
         blen += n;
index a5fd4e6..137812a 100644 (file)
@@ -4,12 +4,14 @@
 assert('String#%') do
   assert_equal "one=1", "one=%d" % 1
   assert_equal "1 one", "%d %s" % [ 1, "one" ]
-  assert_equal "1.0", "%3.1f" % 1.01 if class_defined?("Float")
   assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
   assert_equal 15, ("%b" % (1<<14)).size
+  skip unless Object.const_defined?(:Float)
+  assert_equal "1.0", "%3.1f" % 1.01
 end
 
 assert('String#% with inf') do
+  skip unless Object.const_defined?(:Float)
   inf = Float::INFINITY
 
   assert_equal "Inf", "%f" % inf
@@ -35,9 +37,10 @@ assert('String#% with inf') do
   assert_equal " Inf", "% 3f" % inf
   assert_equal " Inf", "% 4f" % inf
   assert_equal "  Inf", "% 5f" % inf
-end if class_defined?("Float")
+end
 
 assert('String#% with nan') do
+  skip unless Object.const_defined?(:Float)
   nan = Float::NAN
 
   assert_equal "NaN", "%f" % nan
@@ -63,7 +66,7 @@ assert('String#% with nan') do
   assert_equal " NaN", "% 3f" % nan
   assert_equal " NaN", "% 4f" % nan
   assert_equal "  NaN", "% 5f" % nan
-end if class_defined?("Float")
+end
 
 assert("String#% with invalid chr") do
   begin
index 0da84da..311803e 100644 (file)
@@ -1,25 +1,6 @@
 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
   #
@@ -112,7 +93,7 @@ class String
   #    "hello".rstrip!      #=> nil
   #
   def rstrip!
-    raise RuntimeError, "can't modify frozen String" if frozen?
+    raise FrozenError, "can't modify frozen String" if frozen?
     s = self.rstrip
     (s == self) ? nil : self.replace(s)
   end
@@ -142,7 +123,7 @@ class String
   #    "abcdef".casecmp("ABCDEF")    #=> 0
   #
   def casecmp(str)
-    self.downcase <=> str.to_str.downcase
+    self.downcase <=> str.__to_str.downcase
   rescue NoMethodError
     nil
   end
@@ -365,4 +346,98 @@ class String
     self[0, 0] = arg
     self
   end
+
+  ##
+  #  call-seq:
+  #    string.lines                ->  array of string
+  #    string.lines {|s| block}    ->  array of string
+  #
+  #  Returns strings per line;
+  #
+  #    a = "abc\ndef"
+  #    a.lines    #=> ["abc\n", "def"]
+  #
+  #  If a block is given, it works the same as <code>each_line</code>.
+  def lines(&blk)
+    lines = self.__lines
+    if blk
+      lines.each do |line|
+        blk.call(line)
+      end
+    end
+    lines
+  end
+
+  ##
+  #  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"]
+  def upto(max, exclusive=false, &block)
+    return to_enum(:upto, max, exclusive) unless block
+    raise TypeError, "no implicit conversion of #{max.class} into String" unless max.kind_of? String
+
+    len = self.length
+    maxlen = max.length
+    # single character
+    if len == 1 and maxlen == 1
+      c = self.ord
+      e = max.ord
+      while c <= e
+        break if exclusive and c == e
+        yield c.chr
+        c += 1
+      end
+      return self
+    end
+    # both edges are all digits
+    bi = self.to_i(10)
+    ei = max.to_i(10)
+    len = self.length
+    if (bi > 0 or bi == "0"*len) and (ei > 0 or ei == "0"*maxlen)
+      while bi <= ei
+        break if exclusive and bi == ei
+        s = bi.to_s
+        s = s.rjust(len, "0") if s.length < len
+        yield s
+        bi += 1
+      end
+      return self
+    end
+    bs = self
+    while true
+      n = (bs <=> max)
+      break if n > 0
+      break if exclusive and n == 0
+      yield bs
+      break if n == 0
+      bs = bs.succ
+    end
+    self
+  end
 end
index 45e8bb4..ba7e3c6 100644 (file)
@@ -163,7 +163,7 @@ mrb_str_concat_m(mrb_state *mrb, mrb_value self)
   if (mrb_fixnum_p(str))
     str = mrb_fixnum_chr(mrb, str);
   else
-    str = mrb_string_type(mrb, str);
+    str = mrb_ensure_string_type(mrb, str);
   mrb_str_concat(mrb, self, str);
   return self;
 }
@@ -191,7 +191,7 @@ mrb_str_start_with(mrb_state *mrb, mrb_value self)
   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]);
+    sub = mrb_ensure_string_type(mrb, argv[i]);
     mrb_gc_arena_restore(mrb, ai);
     len_l = RSTRING_LEN(self);
     len_r = RSTRING_LEN(sub);
@@ -220,7 +220,7 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
   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]);
+    sub = mrb_ensure_string_type(mrb, argv[i]);
     mrb_gc_arena_restore(mrb, ai);
     len_l = RSTRING_LEN(self);
     len_r = RSTRING_LEN(sub);
@@ -235,6 +235,592 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
   return mrb_false_value();
 }
 
+enum tr_pattern_type {
+  TR_UNINITIALIZED = 0,
+  TR_IN_ORDER  = 1,
+  TR_RANGE = 2,
+};
+
+/*
+  #tr Pattern syntax
+
+  <syntax> ::= (<pattern>)* | '^' (<pattern>)*
+  <pattern> ::= <in order> | <range>
+  <in order> ::= (<ch>)+
+  <range> ::= <ch> '-' <ch>
+*/
+struct tr_pattern {
+  uint8_t type;                // 1:in-order, 2:range
+  mrb_bool flag_reverse : 1;
+  mrb_bool flag_on_heap : 1;
+  uint16_t n;
+  union {
+    uint16_t start_pos;
+    char ch[2];
+  } val;
+  struct tr_pattern *next;
+};
+
+#define STATIC_TR_PATTERN { 0 }
+
+static inline void
+tr_free_pattern(mrb_state *mrb, struct tr_pattern *pat)
+{
+  while (pat) {
+    struct tr_pattern *p = pat->next;
+    if (pat->flag_on_heap) {
+      mrb_free(mrb, pat);
+    }
+    pat = p;
+  }
+}
+
+static struct tr_pattern*
+tr_parse_pattern(mrb_state *mrb, struct tr_pattern *ret, const mrb_value v_pattern, mrb_bool flag_reverse_enable)
+{
+  const char *pattern = RSTRING_PTR(v_pattern);
+  mrb_int pattern_length = RSTRING_LEN(v_pattern);
+  mrb_bool flag_reverse = FALSE;
+  struct tr_pattern *pat1;
+  mrb_int i = 0;
+
+  if(flag_reverse_enable && pattern_length >= 2 && pattern[0] == '^') {
+    flag_reverse = TRUE;
+    i++;
+  }
+
+  while (i < pattern_length) {
+    /* is range pattern ? */
+    mrb_bool const ret_uninit = (ret->type == TR_UNINITIALIZED);
+    pat1 = ret_uninit
+           ? ret
+           : (struct tr_pattern*)mrb_malloc_simple(mrb, sizeof(struct tr_pattern));
+    if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-') {
+      if (pat1 == NULL && ret) {
+      nomem:
+        tr_free_pattern(mrb, ret);
+        mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
+        return NULL;            /* not reached */
+      }
+      pat1->type = TR_RANGE;
+      pat1->flag_reverse = flag_reverse;
+      pat1->flag_on_heap = !ret_uninit;
+      pat1->n = pattern[i+2] - pattern[i] + 1;
+      pat1->next = NULL;
+      pat1->val.ch[0] = pattern[i];
+      pat1->val.ch[1] = pattern[i+2];
+      i += 3;
+    }
+    else {
+      /* in order pattern. */
+      mrb_int start_pos = i++;
+      mrb_int len;
+
+      while (i < pattern_length) {
+       if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-')
+          break;
+       i++;
+      }
+
+      len = i - start_pos;
+      if (len > UINT16_MAX) {
+        mrb_raise(mrb, E_ARGUMENT_ERROR, "tr pattern too long (max 65536)");
+      }
+      if (pat1 == NULL && ret) {
+        goto nomem;
+      }
+      pat1->type = TR_IN_ORDER;
+      pat1->flag_reverse = flag_reverse;
+      pat1->flag_on_heap = !ret_uninit;
+      pat1->n = len;
+      pat1->next = NULL;
+      pat1->val.start_pos = start_pos;
+    }
+
+    if (ret == NULL || ret_uninit) {
+      ret = pat1;
+    }
+    else {
+      struct tr_pattern *p = ret;
+      while (p->next != NULL) {
+        p = p->next;
+      }
+      p->next = pat1;
+    }
+  }
+
+  return ret;
+}
+
+static inline mrb_int
+tr_find_character(const struct tr_pattern *pat, const char *pat_str, int ch)
+{
+  mrb_int ret = -1;
+  mrb_int n_sum = 0;
+  mrb_int flag_reverse = pat ? pat->flag_reverse : 0;
+
+  while (pat != NULL) {
+    if (pat->type == TR_IN_ORDER) {
+      int i;
+      for (i = 0; i < pat->n; i++) {
+       if (pat_str[pat->val.start_pos + i] == ch) ret = n_sum + i;
+      }
+    }
+    else if (pat->type == TR_RANGE) {
+      if (pat->val.ch[0] <= ch && ch <= pat->val.ch[1])
+        ret = n_sum + ch - pat->val.ch[0];
+    }
+    else {
+      mrb_assert(pat->type == TR_UNINITIALIZED);
+    }
+    n_sum += pat->n;
+    pat = pat->next;
+  }
+
+  if (flag_reverse) {
+    return (ret < 0) ? MRB_INT_MAX : -1;
+  }
+  return ret;
+}
+
+static inline mrb_int
+tr_get_character(const struct tr_pattern *pat, const char *pat_str, mrb_int n_th)
+{
+  mrb_int n_sum = 0;
+
+  while (pat != NULL) {
+    if (n_th < (n_sum + pat->n)) {
+      mrb_int i = (n_th - n_sum);
+
+      switch (pat->type) {
+      case TR_IN_ORDER:
+        return pat_str[pat->val.start_pos + i];
+      case TR_RANGE:
+        return pat->val.ch[0]+i;
+      case TR_UNINITIALIZED:
+        return -1;
+      }
+    }
+    if (pat->next == NULL) {
+      switch (pat->type) {
+      case TR_IN_ORDER:
+        return pat_str[pat->val.start_pos + pat->n - 1];
+      case TR_RANGE:
+        return pat->val.ch[1];
+      case TR_UNINITIALIZED:
+        return -1;
+      }
+    }
+    n_sum += pat->n;
+    pat = pat->next;
+  }
+
+  return -1;
+}
+
+static inline void
+tr_bitmap_set(uint8_t bitmap[32], uint8_t ch)
+{
+  uint8_t idx1 = ch / 8;
+  uint8_t idx2 = ch % 8;
+  bitmap[idx1] |= (1<<idx2);
+}
+
+static inline mrb_bool
+tr_bitmap_detect(uint8_t bitmap[32], uint8_t ch)
+{
+  uint8_t idx1 = ch / 8;
+  uint8_t idx2 = ch % 8;
+  if (bitmap[idx1] & (1<<idx2))
+    return TRUE;
+  return FALSE;
+}
+
+/* compile patter to bitmap */
+static void
+tr_compile_pattern(const struct tr_pattern *pat, mrb_value pstr, uint8_t bitmap[32])
+{
+  const char *pattern = RSTRING_PTR(pstr);
+  mrb_int flag_reverse = pat ? pat->flag_reverse : 0;
+  int i;
+
+  for (i=0; i<32; i++) {
+    bitmap[i] = 0;
+  }
+  while (pat != NULL) {
+    if (pat->type == TR_IN_ORDER) {
+      for (i = 0; i < pat->n; i++) {
+        tr_bitmap_set(bitmap, pattern[pat->val.start_pos + i]);
+      }
+    }
+    else if (pat->type == TR_RANGE) {
+      for (i = pat->val.ch[0]; i < pat->val.ch[1]; i++) {
+        tr_bitmap_set(bitmap, i);
+      }
+    }
+    else {
+      mrb_assert(pat->type == TR_UNINITIALIZED);
+    }
+    pat = pat->next;
+  }
+
+  if (flag_reverse) {
+    for (i=0; i<32; i++) {
+      bitmap[i] ^= 0xff;
+    }
+  }
+}
+
+static mrb_bool
+str_tr(mrb_state *mrb, mrb_value str, mrb_value p1, mrb_value p2, mrb_bool squeeze)
+{
+  struct tr_pattern pat = STATIC_TR_PATTERN;
+  struct tr_pattern rep_storage = STATIC_TR_PATTERN;
+  char *s;
+  mrb_int len;
+  mrb_int i;
+  mrb_int j;
+  mrb_bool flag_changed = FALSE;
+  mrb_int lastch = -1;
+  struct tr_pattern *rep;
+
+  mrb_str_modify(mrb, mrb_str_ptr(str));
+  tr_parse_pattern(mrb, &pat, p1, TRUE);
+  rep = tr_parse_pattern(mrb, &rep_storage, p2, FALSE);
+  s = RSTRING_PTR(str);
+  len = RSTRING_LEN(str);
+
+  for (i=j=0; i<len; i++,j++) {
+    mrb_int n = tr_find_character(&pat, RSTRING_PTR(p1), s[i]);
+
+    if (i>j) s[j] = s[i];
+    if (n >= 0) {
+      flag_changed = TRUE;
+      if (rep == NULL) {
+       j--;
+      }
+      else {
+        mrb_int c = tr_get_character(rep, RSTRING_PTR(p2), n);
+
+        if (c < 0 || (squeeze && c == lastch)) {
+          j--;
+          continue;
+        }
+        if (c > 0x80) {
+          mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%S) out of range",
+                     mrb_fixnum_value((mrb_int)c));
+        }
+       lastch = c;
+       s[i] = (char)c;
+      }
+    }
+  }
+
+  tr_free_pattern(mrb, &pat);
+  tr_free_pattern(mrb, rep);
+
+  if (flag_changed) {
+    RSTR_SET_LEN(RSTRING(str), j);
+    RSTRING_PTR(str)[j] = 0;
+  }
+  return flag_changed;
+}
+
+/*
+ * call-seq:
+ *   str.tr(from_str, to_str)   => new_str
+ *
+ * Returns a copy of str with the characters in from_str replaced by the
+ * corresponding characters in to_str.  If to_str is shorter than from_str,
+ * it is padded with its last character in order to maintain the
+ * correspondence.
+ *
+ *  "hello".tr('el', 'ip')      #=> "hippo"
+ *  "hello".tr('aeiou', '*')    #=> "h*ll*"
+ *  "hello".tr('aeiou', 'AA*')  #=> "hAll*"
+ *
+ * Both strings may use the c1-c2 notation to denote ranges of characters,
+ * and from_str may start with a ^, which denotes all characters except
+ * those listed.
+ *
+ *  "hello".tr('a-y', 'b-z')    #=> "ifmmp"
+ *  "hello".tr('^aeiou', '*')   #=> "*e**o"
+ *
+ * The backslash character \ can be used to escape ^ or - and is otherwise
+ * ignored unless it appears at the end of a range or the end of the
+ * from_str or to_str:
+ *
+ *
+ *  "hello^world".tr("\\^aeiou", "*") #=> "h*ll**w*rld"
+ *  "hello-world".tr("a\\-eo", "*")   #=> "h*ll**w*rld"
+ *
+ *  "hello\r\nworld".tr("\r", "")   #=> "hello\nworld"
+ *  "hello\r\nworld".tr("\\r", "")  #=> "hello\r\nwold"
+ *  "hello\r\nworld".tr("\\\r", "") #=> "hello\nworld"
+ *
+ *  "X['\\b']".tr("X\\", "")   #=> "['b']"
+ *  "X['\\b']".tr("X-\\]", "") #=> "'b'"
+ *
+ *  Note: conversion is effective only in ASCII region.
+ */
+static mrb_value
+mrb_str_tr(mrb_state *mrb, mrb_value str)
+{
+  mrb_value dup;
+  mrb_value p1, p2;
+
+  mrb_get_args(mrb, "SS", &p1, &p2);
+  dup = mrb_str_dup(mrb, str);
+  str_tr(mrb, dup, p1, p2, FALSE);
+  return dup;
+}
+
+/*
+ * call-seq:
+ *   str.tr!(from_str, to_str)   -> str or nil
+ *
+ * Translates str in place, using the same rules as String#tr.
+ * Returns str, or nil if no changes were made.
+ */
+static mrb_value
+mrb_str_tr_bang(mrb_state *mrb, mrb_value str)
+{
+  mrb_value p1, p2;
+
+  mrb_get_args(mrb, "SS", &p1, &p2);
+  if (str_tr(mrb, str, p1, p2, FALSE)) {
+    return str;
+  }
+  return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ *   str.tr_s(from_str, to_str)   -> new_str
+ *
+ * Processes a copy of str as described under String#tr, then removes
+ * duplicate characters in regions that were affected by the translation.
+ *
+ *  "hello".tr_s('l', 'r')     #=> "hero"
+ *  "hello".tr_s('el', '*')    #=> "h*o"
+ *  "hello".tr_s('el', 'hx')   #=> "hhxo"
+ */
+static mrb_value
+mrb_str_tr_s(mrb_state *mrb, mrb_value str)
+{
+  mrb_value dup;
+  mrb_value p1, p2;
+
+  mrb_get_args(mrb, "SS", &p1, &p2);
+  dup = mrb_str_dup(mrb, str);
+  str_tr(mrb, dup, p1, p2, TRUE);
+  return dup;
+}
+
+/*
+ * call-seq:
+ *   str.tr_s!(from_str, to_str)   -> str or nil
+ *
+ * Performs String#tr_s processing on str in place, returning
+ * str, or nil if no changes were made.
+ */
+static mrb_value
+mrb_str_tr_s_bang(mrb_state *mrb, mrb_value str)
+{
+  mrb_value p1, p2;
+
+  mrb_get_args(mrb, "SS", &p1, &p2);
+  if (str_tr(mrb, str, p1, p2, TRUE)) {
+    return str;
+  }
+  return mrb_nil_value();
+}
+
+static mrb_bool
+str_squeeze(mrb_state *mrb, mrb_value str, mrb_value v_pat)
+{
+  struct tr_pattern pat_storage = STATIC_TR_PATTERN;
+  struct tr_pattern *pat = NULL;
+  mrb_int i, j;
+  char *s;
+  mrb_int len;
+  mrb_bool flag_changed = FALSE;
+  mrb_int lastch = -1;
+  uint8_t bitmap[32];
+
+  mrb_str_modify(mrb, mrb_str_ptr(str));
+  if (!mrb_nil_p(v_pat)) {
+    pat = tr_parse_pattern(mrb, &pat_storage, v_pat, TRUE);
+    tr_compile_pattern(pat, v_pat, bitmap);
+    tr_free_pattern(mrb, pat);
+  }
+  s = RSTRING_PTR(str);
+  len = RSTRING_LEN(str);
+
+  if (pat) {
+    for (i=j=0; i<len; i++,j++) {
+      if (i>j) s[j] = s[i];
+      if (tr_bitmap_detect(bitmap, s[i]) && s[i] == lastch) {
+        flag_changed = TRUE;
+        j--;
+      }
+      lastch = s[i];
+    }
+  }
+  else {
+    for (i=j=0; i<len; i++,j++) {
+      if (i>j) s[j] = s[i];
+      if (s[i] >= 0 && s[i] == lastch) {
+        flag_changed = TRUE;
+        j--;
+      }
+      lastch = s[i];
+    }
+  }
+
+  if (flag_changed) {
+    RSTR_SET_LEN(RSTRING(str), j);
+    RSTRING_PTR(str)[j] = 0;
+  }
+  return flag_changed;
+}
+
+/*
+ * call-seq:
+ *   str.squeeze([other_str])    -> new_str
+ *
+ * Builds a set of characters from the other_str
+ * parameter(s) using the procedure described for String#count. Returns a
+ * new string where runs of the same character that occur in this set are
+ * replaced by a single character. If no arguments are given, all runs of
+ * identical characters are replaced by a single character.
+ *
+ *  "yellow moon".squeeze                  #=> "yelow mon"
+ *  "  now   is  the".squeeze(" ")         #=> " now is the"
+ *  "putters shoot balls".squeeze("m-z")   #=> "puters shot balls"
+ */
+static mrb_value
+mrb_str_squeeze(mrb_state *mrb, mrb_value str)
+{
+  mrb_value pat = mrb_nil_value();
+  mrb_value dup;
+
+  mrb_get_args(mrb, "|S", &pat);
+  dup = mrb_str_dup(mrb, str);
+  str_squeeze(mrb, dup, pat);
+  return dup;
+}
+
+/*
+ * call-seq:
+ *   str.squeeze!([other_str])   -> str or nil
+ *
+ * Squeezes str in place, returning either str, or nil if no
+ * changes were made.
+ */
+static mrb_value
+mrb_str_squeeze_bang(mrb_state *mrb, mrb_value str)
+{
+  mrb_value pat = mrb_nil_value();
+
+  mrb_get_args(mrb, "|S", &pat);
+  if (str_squeeze(mrb, str, pat)) {
+    return str;
+  }
+  return mrb_nil_value();
+}
+
+static mrb_bool
+str_delete(mrb_state *mrb, mrb_value str, mrb_value v_pat)
+{
+  struct tr_pattern pat = STATIC_TR_PATTERN;
+  mrb_int i, j;
+  char *s;
+  mrb_int len;
+  mrb_bool flag_changed = FALSE;
+  uint8_t bitmap[32];
+
+  mrb_str_modify(mrb, mrb_str_ptr(str));
+  tr_parse_pattern(mrb, &pat, v_pat, TRUE);
+  tr_compile_pattern(&pat, v_pat, bitmap);
+  tr_free_pattern(mrb, &pat);
+
+  s = RSTRING_PTR(str);
+  len = RSTRING_LEN(str);
+
+  for (i=j=0; i<len; i++,j++) {
+    if (i>j) s[j] = s[i];
+    if (tr_bitmap_detect(bitmap, s[i])) {
+      flag_changed = TRUE;
+      j--;
+    }
+  }
+  if (flag_changed) {
+    RSTR_SET_LEN(RSTRING(str), j);
+    RSTRING_PTR(str)[j] = 0;
+  }
+  return flag_changed;
+}
+
+static mrb_value
+mrb_str_delete(mrb_state *mrb, mrb_value str)
+{
+  mrb_value pat;
+  mrb_value dup;
+
+  mrb_get_args(mrb, "S", &pat);
+  dup = mrb_str_dup(mrb, str);
+  str_delete(mrb, dup, pat);
+  return dup;
+}
+
+static mrb_value
+mrb_str_delete_bang(mrb_state *mrb, mrb_value str)
+{
+  mrb_value pat;
+
+  mrb_get_args(mrb, "S", &pat);
+  if (str_delete(mrb, str, pat)) {
+    return str;
+  }
+  return mrb_nil_value();
+}
+
+/*
+ * call_seq:
+ *   str.count([other_str])   -> integer
+ *
+ * Each other_str parameter defines a set of characters to count.  The
+ * intersection of these sets defines the characters to count in str.  Any
+ * other_str that starts with a caret ^ is negated.  The sequence c1-c2
+ * means all characters between c1 and c2.  The backslash character \ can
+ * be used to escape ^ or - and is otherwise ignored unless it appears at
+ * the end of a sequence or the end of a other_str.
+ */
+static mrb_value
+mrb_str_count(mrb_state *mrb, mrb_value str)
+{
+  mrb_value v_pat = mrb_nil_value();
+  mrb_int i;
+  char *s;
+  mrb_int len;
+  mrb_int count = 0;
+  struct tr_pattern pat = STATIC_TR_PATTERN;
+  uint8_t bitmap[32];
+
+  mrb_get_args(mrb, "S", &v_pat);
+  tr_parse_pattern(mrb, &pat, v_pat, TRUE);
+  tr_compile_pattern(&pat, v_pat, bitmap);
+  tr_free_pattern(mrb, &pat);
+  
+  s = RSTRING_PTR(str);
+  len = RSTRING_LEN(str);
+  for (i = 0; i < len; i++) {
+    if (tr_bitmap_detect(bitmap, s[i])) count++;
+  }
+  return mrb_fixnum_value(count);
+}
+
 static mrb_value
 mrb_str_hex(mrb_state *mrb, mrb_value self)
 {
@@ -309,60 +895,6 @@ mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
 
 /*
  *  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;
@@ -524,135 +1056,6 @@ mrb_str_ord(mrb_state* mrb, mrb_value str)
 }
 #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;
-}
-
 /*
  *  call-seq:
  *     str.delete_prefix!(prefix) -> self or nil
@@ -765,6 +1168,31 @@ mrb_str_del_suffix(mrb_state *mrb, mrb_value self)
   return mrb_str_substr(mrb, self, 0, slen-plen);
 }
 
+static mrb_value
+mrb_str_lines(mrb_state *mrb, mrb_value self)
+{
+  mrb_value result;
+  int ai;
+  mrb_int len;
+  char *b = RSTRING_PTR(self);
+  char *p = b, *t;
+  char *e = b + RSTRING_LEN(self);
+
+  mrb_get_args(mrb, "");
+
+  result = mrb_ary_new(mrb);
+  ai = mrb_gc_arena_save(mrb);
+  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;
+}
+
 void
 mrb_mruby_string_ext_gem_init(mrb_state* mrb)
 {
@@ -778,23 +1206,31 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, s, "swapcase",        mrb_str_swapcase,        MRB_ARGS_NONE());
   mrb_define_method(mrb, s, "concat",          mrb_str_concat_m,        MRB_ARGS_REQ(1));
   mrb_define_method(mrb, s, "<<",              mrb_str_concat_m,        MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "count",           mrb_str_count,           MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "tr",              mrb_str_tr,              MRB_ARGS_REQ(2));
+  mrb_define_method(mrb, s, "tr!",             mrb_str_tr_bang,         MRB_ARGS_REQ(2));
+  mrb_define_method(mrb, s, "tr_s",            mrb_str_tr_s,            MRB_ARGS_REQ(2));
+  mrb_define_method(mrb, s, "tr_s!",           mrb_str_tr_s_bang,       MRB_ARGS_REQ(2));
+  mrb_define_method(mrb, s, "squeeze",         mrb_str_squeeze,         MRB_ARGS_OPT(1));
+  mrb_define_method(mrb, s, "squeeze!",        mrb_str_squeeze_bang,    MRB_ARGS_OPT(1));
+  mrb_define_method(mrb, s, "delete",          mrb_str_delete,          MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "delete!",         mrb_str_delete_bang,     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_alias(mrb,  s, "next",            "succ");
+  mrb_define_alias(mrb,  s, "next!",           "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, s, "delete_prefix!",  mrb_str_del_prefix_bang, MRB_ARGS_REQ(1));
   mrb_define_method(mrb, s, "delete_prefix",   mrb_str_del_prefix,      MRB_ARGS_REQ(1));
   mrb_define_method(mrb, s, "delete_suffix!",  mrb_str_del_suffix_bang, MRB_ARGS_REQ(1));
   mrb_define_method(mrb, s, "delete_suffix",   mrb_str_del_suffix,      MRB_ARGS_REQ(1));
 
+  mrb_define_method(mrb, s, "__lines",         mrb_str_lines,           MRB_ARGS_NONE());
   mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
 }
 
index b6146fb..44ca1fd 100644 (file)
@@ -4,13 +4,6 @@
 
 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]
@@ -31,18 +24,6 @@ assert('String#setbyte') do
   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))
@@ -52,62 +33,72 @@ assert('String#byteslice') do
 end
 
 assert('String#dump') do
-  ("\1" * 100).dump     # should not raise an exception - regress #1210
-  "\0".inspect == "\"\\000\"" and
-  "foo".dump == "\"foo\""
+  assert_equal("\"\\x00\"", "\0".dump)
+  assert_equal("\"foo\"", "foo".dump)
+  assert_nothing_raised { ("\1" * 100).dump }   # regress #1210
 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  "
+  assert_equal("abc", s.strip)
+  assert_equal("  abc  ", s)
+  assert_equal("", "".strip)
+  assert_equal("", " \t\r\n\f\v".strip)
+  assert_equal("\0a", "\0a\0".strip)
+  assert_equal("abc", "abc".strip)
+  assert_equal("abc", "  abc".strip)
+  assert_equal("abc", "abc  ".strip)
 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  "
+  assert_equal("abc  ", s.lstrip)
+  assert_equal("  abc  ", s)
+  assert_equal("", "".lstrip)
+  assert_equal("", " \t\r\n\f\v".lstrip)
+  assert_equal("\0a\0", "\0a\0".lstrip)
+  assert_equal("abc", "abc".lstrip)
+  assert_equal("abc", "  abc".lstrip)
+  assert_equal("abc  ", "abc  ".lstrip)
 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  "
+  assert_equal("  abc", s.rstrip)
+  assert_equal("  abc  ", s)
+  assert_equal("", "".rstrip)
+  assert_equal("", " \t\r\n\f\v".rstrip)
+  assert_equal("\0a", "\0a\0".rstrip)
+  assert_equal("abc", "abc".rstrip)
+  assert_equal("  abc", "  abc".rstrip)
+  assert_equal("abc", "abc  ".rstrip)
 end
 
 assert('String#strip!') do
   s = "  abc  "
   t = "abc"
-  s.strip! == "abc" and s == "abc" and t.strip! == nil
+  assert_equal("abc", s.strip!)
+  assert_equal("abc", s)
+  assert_nil(t.strip!)
+  assert_equal("abc", t)
 end
 
 assert('String#lstrip!') do
   s = "  abc  "
   t = "abc  "
-  s.lstrip! == "abc  " and s == "abc  " and t.lstrip! == nil
+  assert_equal("abc  ", s.lstrip!)
+  assert_equal("abc  ", s)
+  assert_nil(t.lstrip!)
+  assert_equal("abc  ", t)
 end
 
 assert('String#rstrip!') do
   s = "  abc  "
   t = "  abc"
-  s.rstrip! == "  abc" and s == "  abc" and t.rstrip! == nil
+  assert_equal("  abc", s.rstrip!)
+  assert_equal("  abc", s)
+  assert_nil(t.rstrip!)
+  assert_equal("  abc", t)
 end
 
 assert('String#swapcase') do
@@ -126,12 +117,6 @@ 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
 
@@ -140,11 +125,69 @@ assert('String#casecmp') do
   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#count') do
+  s = "abccdeff123"
+  assert_equal 0, s.count("")
+  assert_equal 1, s.count("a")
+  assert_equal 2, s.count("ab")
+  assert_equal 9, s.count("^c")
+  assert_equal 8, s.count("a-z")
+  assert_equal 4, s.count("a0-9")
+end
+
+assert('String#tr') do
+  assert_equal "ABC", "abc".tr('a-z', 'A-Z')
+  assert_equal "hippo", "hello".tr('el', 'ip')
+  assert_equal "Ruby", "Lisp".tr("Lisp", "Ruby")
+  assert_equal "*e**o", "hello".tr('^aeiou', '*')
+  assert_equal "heo", "hello".tr('l', '')
+end
+
+assert('String#tr!') do
+  s = "abcdefghijklmnopqR"
+  assert_equal "ab12222hijklmnopqR", s.tr!("cdefg", "12")
+  assert_equal "ab12222hijklmnopqR", s
+end
+
+assert('String#tr_s') do
+  assert_equal "hero", "hello".tr_s('l', 'r')
+  assert_equal "h*o", "hello".tr_s('el', '*')
+  assert_equal "hhxo", "hello".tr_s('el', 'hx')
+end
+
+assert('String#tr_s!') do
+  s = "hello"
+  assert_equal "hero", s.tr_s!('l', 'r')
+  assert_equal "hero", s
+  assert_nil s.tr_s!('l', 'r')
+end
+
+assert('String#squeeze') do
+  assert_equal "yelow mon", "yellow moon".squeeze
+  assert_equal " now is the", "  now   is  the".squeeze(" ")
+  assert_equal "puters shot balls", "putters shoot balls".squeeze("m-z")
+end
+
+assert('String#squeeze!') do
+  s = "  now   is  the"
+  assert_equal " now is the", s.squeeze!(" ")
+  assert_equal " now is the", s
+end
+
+assert('String#delete') do
+  assert_equal "he", "hello".delete("lo")
+  assert_equal "hll", "hello".delete("aeiou")
+  assert_equal "ll", "hello".delete("^l")
+  assert_equal "ho", "hello".delete("ej-m")
+end
+
+assert('String#delete!') do
+  s = "hello"
+  assert_equal "he", s.delete!("lo")
+  assert_equal "he", s
+  assert_nil s.delete!("lz")
 end
 
 assert('String#start_with?') do
index 7cf3dd3..c5b5354 100644 (file)
@@ -81,23 +81,22 @@ if Object.const_defined?(:Struct)
     # 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
+    ##
+    # 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
 end
-
index adeb09b..c0ce712 100644 (file)
@@ -24,19 +24,18 @@ struct_class(mrb_state *mrb)
 }
 
 static inline mrb_value
-struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
+struct_ivar_get(mrb_state *mrb, mrb_value cls, mrb_sym id)
 {
-  struct RClass* kclass;
+  struct RClass* c = mrb_class_ptr(cls);
   struct RClass* sclass = struct_class(mrb);
   mrb_value ans;
 
   for (;;) {
-    ans = mrb_iv_get(mrb, c, id);
+    ans = mrb_iv_get(mrb, mrb_obj_value(c), id);
     if (!mrb_nil_p(ans)) return ans;
-    kclass = RCLASS_SUPER(c);
-    if (kclass == 0 || kclass == sclass)
+    c = c->super;
+    if (c == sclass || c == 0)
       return mrb_nil_value();
-    c = mrb_obj_value(kclass);
   }
 }
 
@@ -214,7 +213,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *kl
   }
   else {
     /* old style: should we warn? */
-    name = mrb_str_to_str(mrb, name);
+    mrb_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);
@@ -303,17 +302,19 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
       }
     }
     rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
-    for (i=0; i<RARRAY_LEN(rest); i++) {
+    for (i=0; i<argcnt; 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));
-  }
+    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;
+    return st;
+  }
+  /* not reached */
+  return mrb_nil_value();
 }
 
 static mrb_int
@@ -398,7 +399,7 @@ struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
       return ptr[i];
     }
   }
-  mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
+  mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
   return mrb_nil_value();       /* not reached */
 }
 
index 982e344..c298fef 100644 (file)
@@ -199,7 +199,7 @@ assert 'Struct#freeze' do
   assert_equal :test, o.m
 
   o.freeze
-  assert_raise(RuntimeError) { o.m = :modify }
-  assert_raise(RuntimeError) { o[:m] = :modify }
+  assert_raise(FrozenError) { o.m = :modify }
+  assert_raise(FrozenError) { o[:m] = :modify }
   assert_equal :test, o.m
 end
index 28cce31..99fa275 100644 (file)
@@ -3,12 +3,6 @@ class Symbol
 
   alias intern to_sym
 
-  def to_proc
-    ->(obj,*args,&block) do
-      obj.__send__(self, *args, &block)
-    end
-  end
-
   ##
   # call-seq:
   #   sym.capitalize  -> symbol
index a992dbf..ccb2971 100644 (file)
@@ -1,11 +1,7 @@
 #include <mruby.h>
 #include <mruby/khash.h>
 #include <mruby/array.h>
-
-typedef struct symbol_name {
-  size_t len;
-  const char *name;
-} symbol_name;
+#include <mruby/string.h>
 
 /*
  *  call-seq:
@@ -22,6 +18,7 @@ typedef struct symbol_name {
  *                                     :Tms, :getwd, :$=, :ThreadGroup,
  *                                     :wait2, :$>]
  */
+#ifdef MRB_ENABLE_ALL_SYMBOLS
 static mrb_value
 mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
 {
@@ -29,11 +26,13 @@ mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
   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));
+    mrb_sym sym = i<<1;
+    mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
   }
 
   return ary;
 }
+#endif
 
 /*
  * call-seq:
@@ -45,7 +44,13 @@ static mrb_value
 mrb_sym_length(mrb_state *mrb, mrb_value self)
 {
   mrb_int len;
+#ifdef MRB_UTF8_STRING
+  mrb_int byte_len;
+  const char *name = mrb_sym2name_len(mrb, mrb_symbol(self), &byte_len);
+  len = mrb_utf8_len(name, byte_len);
+#else
   mrb_sym2name_len(mrb, mrb_symbol(self), &len);
+#endif
   return mrb_fixnum_value(len);
 }
 
@@ -53,7 +58,9 @@ void
 mrb_mruby_symbol_ext_gem_init(mrb_state* mrb)
 {
   struct RClass *s = mrb->symbol_class;
+#ifdef MRB_ENABLE_ALL_SYMBOLS
   mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE());
+#endif
   mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE());
   mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE());
 }
index 6070d14..61ecad2 100644 (file)
@@ -1,19 +1,27 @@
+# coding: utf-8
 ##
 # 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
+if Symbol.respond_to?(:all_symbols)
+  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
+end
+
+%w[size length].each do |n|
+  assert("Symbol##{n}") do
+    assert_equal 5, :hello.__send__(n)
+    assert_equal 4, :"aA\0b".__send__(n)
+    if "あ".size == 1  # enable MRB_UTF8_STRING?
+      assert_equal 8, :"こんにちは世界!".__send__(n)
+      assert_equal 4, :"aあ\0b".__send__(n)
+    else
+      assert_equal 22, :"こんにちは世界!".__send__(n)
+      assert_equal 6, :"aあ\0b".__send__(n)
+    end
+  end
 end
 
 assert("Symbol#capitalize") do
index 434d1fe..fd180b1 100644 (file)
@@ -18,8 +18,9 @@
 #include <mruby/variable.h>
 #include <mruby/array.h>
 
-void
-mrb_init_mrbtest(mrb_state *);
+extern const uint8_t mrbtest_assert_irep[];
+
+void mrbgemtest_init(mrb_state* mrb);
 
 /* Print a short remark for the user */
 static void
@@ -29,56 +30,36 @@ print_hint(void)
 }
 
 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);
+  mrb_value result = 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;
+  else {
+    return mrb_bool(result) ? EXIT_SUCCESS : EXIT_FAILURE;
   }
-  return EXIT_SUCCESS;
 }
 
-static void
-t_printstr(mrb_state *mrb, mrb_value obj)
+/* Implementation of print due to the reason that there might be no print */
+static mrb_value
+t_print(mrb_state *mrb, mrb_value self)
 {
-  char *s;
-  mrb_int len;
-
-  if (mrb_string_p(obj)) {
-    s = RSTRING_PTR(obj);
-    len = RSTRING_LEN(obj);
-    fwrite(s, len, 1, stdout);
-    fflush(stdout);
-  }
-}
+  mrb_value *argv;
+  mrb_int argc;
 
-mrb_value
-mrb_t_printstr(mrb_state *mrb, mrb_value self)
-{
-  mrb_value argv;
-
-  mrb_get_args(mrb, "o", &argv);
-  t_printstr(mrb, argv);
+  mrb_get_args(mrb, "*!", &argv, &argc);
+  for (mrb_int i = 0; i < argc; ++i) {
+    mrb_value s = mrb_obj_as_string(mrb, argv[i]);
+    fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stdout);
+  }
+  fflush(stdout);
 
-  return argv;
+  return mrb_nil_value();
 }
 
 void
@@ -87,7 +68,7 @@ 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));
+  mrb_define_method(mrb, krn, "t_print", t_print, MRB_ARGS_ANY());
 
   mrbtest = mrb_define_module(mrb, "Mrbtest");
 
@@ -130,6 +111,7 @@ mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
   TEST_COUNT_PASS(ok_test);
   TEST_COUNT_PASS(ko_test);
   TEST_COUNT_PASS(kill_test);
+  TEST_COUNT_PASS(skip_test);
 
 #undef TEST_COUNT_PASS
 
@@ -167,7 +149,8 @@ main(int argc, char **argv)
   }
 
   mrb_init_test_driver(mrb, verbose);
-  mrb_init_mrbtest(mrb);
+  mrb_load_irep(mrb, mrbtest_assert_irep);
+  mrbgemtest_init(mrb);
   ret = eval_test(mrb);
   mrb_close(mrb);
 
diff --git a/third-party/mruby/mrbgems/mruby-test/init_mrbtest.c b/third-party/mruby/mrbgems/mruby-test/init_mrbtest.c
deleted file mode 100644 (file)
index 7678a52..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#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);
-    mrb_close(mrb);
-    exit(EXIT_FAILURE);
-  }
-  mrb_close(core_test);
-}
-
index 7da0b5e..dcb7bb7 100644 (file)
@@ -7,23 +7,16 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
   spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
 
   spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
-  if build.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT")
-    spec.test_rbfiles.delete("#{MRUBY_ROOT}/test/t/float.rb")
-  end
-
 
   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"
+  driver = "#{spec.dir}/driver.c"
 
   assert_c = "#{build_dir}/assert.c"
   assert_rb = "#{MRUBY_ROOT}/test/assert.rb"
@@ -31,7 +24,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
   mrbtest_objs << assert_lib
 
   file assert_lib => assert_c
-  file assert_c => assert_rb do |t|
+  file assert_c => [assert_rb, build.mrbcfile] do |t|
     open(t.name, 'w') do |f|
       mrbc.run f, assert_rb, 'mrbtest_assert_irep'
     end
@@ -45,7 +38,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
     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|
+    file g.test_rbireps => [g.test_rbfiles, build.mrbcfile].flatten do |t|
       FileUtils.mkdir_p File.dirname(t.name)
       open(t.name, 'w') do |f|
         g.print_gem_test_header(f)
@@ -140,32 +133,36 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
   end
 
   unless build.build_mrbtest_lib_only?
-    file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t|
+    file exec => [driver_obj, mlib, mrbtest_lib, build.libmruby_static] 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
+      build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags,
+                       gem_flags_before_libraries, gem_flags_after_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
+  active_gems_path = "#{build_dir}/active_gems_path.lst"
+  active_gem_list = if File.exist? active_gems_path
+                      File.read active_gems_path
+                    else
+                      FileUtils.mkdir_p File.dirname(active_gems_path)
+                      nil
+                    end
+  current_gem_list = build.gems.map(&:name).join("\n")
+  task active_gems_path do |_t|
+    FileUtils.mkdir_p File.dirname(active_gems_path)
+    File.write active_gems_path, current_gem_list
   end
-  file clib => active_gems
+  file clib => active_gems_path if active_gem_list != current_gem_list
 
   file mlib => clib
-  file clib => init do |t|
+  file clib => [build.mrbcfile, __FILE__] do |_t|
     _pp "GEN", "*.rb", "#{clib.relative_path}"
     FileUtils.mkdir_p File.dirname(clib)
     open(clib, 'w') do |f|
@@ -178,7 +175,8 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
       f.puts %Q[ *   All manual changes will get lost.]
       f.puts %Q[ */]
       f.puts %Q[]
-      f.puts IO.read(init)
+      f.puts %Q[struct mrb_state;]
+      f.puts %Q[typedef struct mrb_state mrb_state;]
       build.gems.each do |g|
         f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
       end
diff --git a/third-party/mruby/mrbgems/mruby-time/include/mruby/time.h b/third-party/mruby/mrbgems/mruby-time/include/mruby/time.h
new file mode 100644 (file)
index 0000000..d71f4cc
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+** mruby/time.h - Time class
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_TIME_H
+#define MRUBY_TIME_H
+
+#include "mruby/common.h"
+
+MRB_BEGIN_DECL
+
+typedef enum mrb_timezone {
+  MRB_TIMEZONE_NONE   = 0,
+  MRB_TIMEZONE_UTC    = 1,
+  MRB_TIMEZONE_LOCAL  = 2,
+  MRB_TIMEZONE_LAST   = 3
+} mrb_timezone;
+
+MRB_API mrb_value mrb_time_at(mrb_state *mrb, double sec, double usec, mrb_timezone timezone);
+
+MRB_END_DECL
+
+#endif /* MRUBY_TIME_H */
index cfd51ac..7f6c300 100644 (file)
@@ -9,6 +9,7 @@
 #include <mruby.h>
 #include <mruby/class.h>
 #include <mruby/data.h>
+#include <mruby/time.h>
 
 #ifndef MRB_DISABLE_STDIO
 #include <stdio.h>
@@ -46,7 +47,7 @@ double round(double x) {
 /* #define NO_GMTIME_R */
 
 #ifdef _WIN32
-#if _MSC_VER
+#ifdef _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)
@@ -166,13 +167,6 @@ timegm(struct tm *tm)
 * 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;
@@ -204,9 +198,10 @@ struct mrb_time {
 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. */
+    seconds setting. Returns self on success, NULL of failure.
+    if `dealloc` is set `true`, it frees `self` on error. */
 static struct mrb_time*
-time_update_datetime(mrb_state *mrb, struct mrb_time *self)
+time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc)
 {
   struct tm *aid;
 
@@ -217,7 +212,10 @@ time_update_datetime(mrb_state *mrb, struct mrb_time *self)
     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));
+    mrb_float sec = (mrb_float)self->sec;
+
+    mrb_free(mrb, self);
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
     /* not reached */
     return NULL;
   }
@@ -269,17 +267,17 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
   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 */
+    long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */
     tm->usec -= sec2 * 1000000;
     tm->sec += sec2;
   }
   else if (tm->usec >= 1000000) {
-    long sec2 = (long)(usec / 1000000);
+    long sec2 = (long)(tm->usec / 1000000);
     tm->usec -= sec2 * 1000000;
     tm->sec += sec2;
   }
   tm->timezone = timezone;
-  time_update_datetime(mrb, tm);
+  time_update_datetime(mrb, tm, TRUE);
 
   return tm;
 }
@@ -296,7 +294,7 @@ current_mrb_time(mrb_state *mrb)
   struct mrb_time *tm;
 
   tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
-#if defined(TIME_UTC)
+#if defined(TIME_UTC) && !defined(__ANDROID__)
   {
     struct timespec ts;
     if (timespec_get(&ts, TIME_UTC) == 0) {
@@ -331,7 +329,7 @@ current_mrb_time(mrb_state *mrb)
   }
 #endif
   tm->timezone = MRB_TIMEZONE_LOCAL;
-  time_update_datetime(mrb, tm);
+  time_update_datetime(mrb, tm, TRUE);
 
   return tm;
 }
@@ -343,10 +341,16 @@ mrb_time_now(mrb_state *mrb, mrb_value self)
   return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb));
 }
 
+MRB_API mrb_value
+mrb_time_at(mrb_state *mrb, double sec, double usec, enum mrb_timezone zone)
+{
+  return mrb_time_make(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
+}
+
 /* 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_time_at_m(mrb_state *mrb, mrb_value self)
 {
   mrb_float f, f2 = 0;
 
@@ -616,7 +620,7 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self)
   tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
   *tm2 = *tm;
   tm2->timezone = MRB_TIMEZONE_UTC;
-  time_update_datetime(mrb, tm2);
+  time_update_datetime(mrb, tm2, TRUE);
   return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
 }
 
@@ -631,7 +635,7 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self)
   tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
   *tm2 = *tm;
   tm2->timezone = MRB_TIMEZONE_LOCAL;
-  time_update_datetime(mrb, tm2);
+  time_update_datetime(mrb, tm2, TRUE);
   return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
 }
 
@@ -709,7 +713,7 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self)
 
   tm = time_get_ptr(mrb, self);
   tm->timezone = MRB_TIMEZONE_LOCAL;
-  time_update_datetime(mrb, tm);
+  time_update_datetime(mrb, tm, FALSE);
   return self;
 }
 
@@ -806,7 +810,7 @@ mrb_time_utc(mrb_state *mrb, mrb_value self)
 
   tm = time_get_ptr(mrb, self);
   tm->timezone = MRB_TIMEZONE_UTC;
-  time_update_datetime(mrb, tm);
+  time_update_datetime(mrb, tm, FALSE);
   return self;
 }
 
@@ -830,7 +834,7 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
   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, "at", mrb_time_at_m, 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 */
index aebdd8b..26aae9a 100644 (file)
@@ -16,8 +16,8 @@ assert('Toplevel#include') do
 
   self.include ToplevelTestModule2, ToplevelTestModule1
 
-  assert_true self.class.included_modules.include?( ToplevelTestModule1 )
-  assert_true self.class.included_modules.include?( ToplevelTestModule2 )
+  assert_true self.class.ancestors.include?( ToplevelTestModule1 )
+  assert_true self.class.ancestors.include?( ToplevelTestModule2 )
   assert_equal :foo, method_foo
   assert_equal :bar2, CONST_BAR
 end
index d767563..d598efc 100644 (file)
@@ -66,7 +66,7 @@ class Array
   #
   # 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
+    size = size.__to_int
     raise ArgumentError, "negative array size" if size < 0
 
     self.clear
@@ -84,8 +84,15 @@ class Array
   end
 
   def _inspect
-    return "[]" if self.size == 0
-    "["+self.map{|x|x.inspect}.join(", ")+"]"
+    size = self.size
+    return "[]" if size == 0
+    ary=[]
+    i=0
+    while i<size
+      ary<<self[i].inspect
+      i+=1
+    end
+    "["+ary.join(", ")+"]"
   end
   ##
   # Return the contents of this array as a string.
@@ -193,39 +200,69 @@ class Array
   include Enumerable
 
   ##
-  # Quick sort
-  # left  : the beginning of sort region
-  # right : the end of sort region
-  def __sort_sub__(left, right, &block)
-    if left < right
-      i = left
-      j = right
-      pivot = self[i + (j - i) / 2]
-      while true
-        while ((block)? block.call(self[i], pivot): (self[i] <=> pivot)) < 0
-          i += 1
-        end
-        while ((block)? block.call(pivot, self[j]): (pivot <=> self[j])) < 0
-          j -= 1
-        end
-        break if (i >= j)
-        tmp = self[i]; self[i] = self[j]; self[j] = tmp;
-        i += 1
-        j -= 1
-      end
-      __sort_sub__(left, i-1, &block)
-      __sort_sub__(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__(0, size - 1, &block)
+    stack = [ [ 0, self.size - 1 ] ]
+    until stack.empty?
+      left, mid, right = stack.pop
+      if right == nil
+        right = mid
+        # sort self[left..right]
+        if left < right
+          if left + 1 == right
+            lval = self[left]
+            rval = self[right]
+            cmp = if block then block.call(lval,rval) else lval <=> rval end
+            if cmp.nil?
+              raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
+            end
+            if cmp > 0
+              self[left]  = rval
+              self[right] = lval
+            end
+          else
+            mid = ((left + right + 1) / 2).floor
+            stack.push [ left, mid, right ]
+            stack.push [ mid, right ]
+            stack.push [ left, (mid - 1) ] if left < mid - 1
+          end
+        end
+      else
+        lary = self[left, mid - left]
+        lsize = lary.size
+
+        # The entity sharing between lary and self may cause a large memory
+        # copy operation in the merge loop below.  This harmless operation
+        # cancels the sharing and provides a huge performance gain.
+        lary[0] = lary[0]
+
+        # merge
+        lidx = 0
+        ridx = mid
+        (left..right).each { |i|
+          if lidx >= lsize
+            break
+          elsif ridx > right
+            self[i, lsize - lidx] = lary[lidx, lsize - lidx]
+            break
+          else
+            lval = lary[lidx]
+            rval = self[ridx]
+            cmp = if block then block.call(lval,rval) else lval <=> rval end
+            if cmp.nil?
+              raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
+            end
+            if cmp <= 0
+              self[i] = lval
+              lidx += 1
+            else
+              self[i] = rval
+              ridx += 1
+            end
+          end
+        }
+      end
     end
     self
   end
index a38f89d..9bd74e1 100644 (file)
@@ -13,6 +13,8 @@
 # @ISO 15.3.2
 module Enumerable
 
+  NONE = Object.new
+
   ##
   # Call the given block for each element
   # which is yield by +each+. Return false
@@ -339,8 +341,7 @@ module Enumerable
     h = 12347
     i = 0
     self.each do |e|
-      n = (e.hash & (0x7fffffff >> (i % 16))) << (i % 16)
-      h ^= n
+      h = __update_hash(h, i, e.hash)
       i += 1
     end
     h
diff --git a/third-party/mruby/mrblib/float.rb b/third-party/mruby/mrblib/float.rb
deleted file mode 100644 (file)
index 2b86dc1..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-##
-# 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 if class_defined?("Float")
index 6b4803c..609883e 100644 (file)
@@ -12,9 +12,7 @@ class Hash
   # ISO 15.2.13.4.1
   def ==(hash)
     return true if self.equal?(hash)
-    begin
-      hash = hash.to_hash
-    rescue NoMethodError
+    unless Hash === hash
       return false
     end
     return false if self.size != hash.size
@@ -32,9 +30,7 @@ class Hash
   # ISO 15.2.13.4.32 (x)
   def eql?(hash)
     return true if self.equal?(hash)
-    begin
-      hash = hash.to_hash
-    rescue NoMethodError
+    unless Hash === hash
       return false
     end
     return false if self.size != hash.size
@@ -55,10 +51,9 @@ class Hash
   # ISO 15.2.13.4.8
   def delete(key, &block)
     if block && !self.has_key?(key)
-      block.call(key)
-    else
-      self.__delete(key)
+      return block.call(key)
     end
+    self.__delete(key)
   end
 
   ##
@@ -154,9 +149,8 @@ class Hash
   #
   # ISO 15.2.13.4.23
   def replace(hash)
-    raise TypeError, "can't convert argument into Hash" unless hash.respond_to?(:to_hash)
+    raise TypeError, "Hash required (#{hash.class} given)" unless Hash === hash
     self.clear
-    hash = hash.to_hash
     hash.each_key{|k|
       self[k] = hash[k]
     }
@@ -179,10 +173,8 @@ class Hash
   #
   # 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]}
+    raise TypeError, "Hash required (#{other.class} given)" unless Hash === other
+    h = self.dup
     if block
       other.each_key{|k|
         h[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k]
@@ -196,9 +188,16 @@ class Hash
   # internal method for Hash inspection
   def _inspect
     return "{}" if self.size == 0
-    "{"+self.map {|k,v|
-      k._inspect + "=>" + v._inspect
-    }.join(", ")+"}"
+    ary=[]
+    keys=self.keys
+    size=keys.size
+    i=0
+    while i<size
+      k=keys[i]
+      ary<<(k._inspect + "=>" + self[k]._inspect)
+      i+=1
+    end
+    "{"+ary.join(", ")+"}"
   end
   ##
   # Return the contents of this hash as a string.
@@ -319,34 +318,6 @@ class Hash
     }
     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
 
 ##
index 550ae81..4700684 100644 (file)
@@ -6,7 +6,7 @@ module Kernel
 
   # 15.3.1.2.1 Kernel.`
   # provided by Kernel#`
-  # 15.3.1.3.5
+  # 15.3.1.3.3
   def `(s)
     raise NotImplementedError.new("backquotes not implemented")
   end
index fe4aae1..e96decb 100644 (file)
@@ -3,14 +3,11 @@ MRuby.each_target do
   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")
+  self.libmruby_objs << 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
-    if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT")
-      rbfiles.delete("#{current_dir}/float.rb")
-    end
     FileUtils.mkdir_p File.dirname(t.name)
     open(t.name, 'w') do |f|
       _pp "GEN", "*.rb", "#{t.name.relative_path}"
index 1b11e92..5a3c5eb 100644 (file)
@@ -104,11 +104,11 @@ module Integral
     raise ArgumentError, "step can't be 0" if step == 0
     return to_enum(:step, num, step) unless block
 
-    i = if class_defined?("Float") && num.kind_of?(Float) then self.to_f else self end
+    i = __coerce_step_counter(num, step)
     if num == nil
       while true
         block.call(i)
-        i+=step
+        i += step
       end
       return self
     end
index ee98cfa..64e85c5 100644 (file)
@@ -12,7 +12,7 @@ class String
   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
+    rs.__to_str
     offset = 0
     rs_len = rs.length
     this = dup
@@ -67,7 +67,7 @@ class String
       block = nil
     end
     if !replace.nil? || !block
-      replace = replace.to_str
+      replace.__to_str
     end
     offset = 0
     result = []
@@ -99,7 +99,7 @@ class String
     raise FrozenError, "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
+    return nil unless self.index(args[0])
     self.replace(str)
   end
 
@@ -129,12 +129,12 @@ class String
     end
 
     pattern, replace = *args
-    pattern = pattern.to_str
+    pattern.__to_str
     if args.length == 2 && block
       block = nil
     end
     unless block
-      replace = replace.to_str
+      replace.__to_str
     end
     result = []
     this = dup
@@ -161,7 +161,7 @@ class String
   def sub!(*args, &block)
     raise FrozenError, "can't modify frozen String" if frozen?
     str = self.sub(*args, &block)
-    return nil if str == self
+    return nil unless self.index(args[0])
     self.replace(str)
   end
 
@@ -245,14 +245,13 @@ class String
   ##
   # 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 String === re
       if Object.const_defined?(:Regexp)
         r = Regexp.new(re)
         r.match(self, &block)
diff --git a/third-party/mruby/mrblib/symbol.rb b/third-party/mruby/mrblib/symbol.rb
new file mode 100644 (file)
index 0000000..9c981dd
--- /dev/null
@@ -0,0 +1,7 @@
+class Symbol
+  def to_proc
+    ->(obj,*args,&block) do
+      obj.__send__(self, *args, &block)
+    end
+  end
+end
index bfa6e5f..707820a 100644 (file)
@@ -231,7 +231,7 @@ ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
 static void
 ary_shrink_capa(mrb_state *mrb, struct RArray *a)
 {
-  
+
   mrb_int capa;
 
   if (ARY_EMBED_P(a)) return;
@@ -377,6 +377,9 @@ ary_replace(mrb_state *mrb, struct RArray *a, struct RArray *b)
     if (ARY_EMBED_P(a)) {
       ARY_UNSET_EMBED_FLAG(a);
     }
+    else {
+      mrb_free(mrb, a->as.heap.ptr);
+    }
     a->as.heap.ptr = b->as.heap.ptr;
     a->as.heap.len = len;
     a->as.heap.aux.shared = b->as.heap.aux.shared;
@@ -850,14 +853,14 @@ 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_int i, len, alen;
   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) {
+      if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == 1) {
         return ary_subseq(mrb, a, i, len);
       }
       else {
@@ -871,6 +874,7 @@ mrb_ary_aget(mrb_state *mrb, mrb_value self)
   }
 
   i = aget_index(mrb, index);
+  alen = ARY_LEN(a);
   if (i < 0) i += alen;
   if (i < 0 || alen < i) return mrb_nil_value();
   if (len < 0) return mrb_nil_value();
@@ -950,9 +954,10 @@ mrb_ary_delete_at(mrb_state *mrb, mrb_value self)
   mrb_int   index;
   mrb_value val;
   mrb_value *ptr;
-  mrb_int len, alen = ARY_LEN(a);
+  mrb_int len, alen;
 
   mrb_get_args(mrb, "i", &index);
+  alen = ARY_LEN(a);
   if (index < 0) index += alen;
   if (index < 0 || alen <= index) return mrb_nil_value();
 
@@ -977,16 +982,17 @@ 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);
+  mrb_int size, alen;
 
   if (mrb_get_argc(mrb) == 0) {
-    return (alen > 0)? ARY_PTR(a)[0]: mrb_nil_value();
+    return (ARY_LEN(a) > 0)? ARY_PTR(a)[0]: mrb_nil_value();
   }
   mrb_get_args(mrb, "|i", &size);
   if (size < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
   }
 
+  alen = ARY_LEN(a);
   if (size > alen) size = alen;
   if (ARY_SHARED_P(a)) {
     return ary_subseq(mrb, a, 0, size);
@@ -998,10 +1004,13 @@ 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);
+  mrb_int n, size, alen;
 
-  if (mrb_get_args(mrb, "|i", &size) == 0)
-    return (alen > 0)? ARY_PTR(a)[alen - 1]: mrb_nil_value();
+  n = mrb_get_args(mrb, "|i", &size);
+  alen = ARY_LEN(a);
+  if (n == 0) {
+    return (alen > 0) ? ARY_PTR(a)[alen - 1]: mrb_nil_value();
+  }
 
   if (size < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
@@ -1049,7 +1058,7 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
 MRB_API mrb_value
 mrb_ary_splat(mrb_state *mrb, mrb_value v)
 {
-  mrb_value a, recv_class;
+  mrb_value a;
 
   if (mrb_array_p(v)) {
     return v;
@@ -1060,22 +1069,11 @@ mrb_ary_splat(mrb_state *mrb, mrb_value v)
   }
 
   a = mrb_funcall(mrb, v, "to_a", 0);
-  if (mrb_array_p(a)) {
-    return a;
-  }
-  else if (mrb_nil_p(a)) {
+  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();
-  }
+  mrb_ensure_array_type(mrb, a);
+  return a;
 }
 
 static mrb_value
@@ -1105,6 +1103,13 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self)
 }
 
 static mrb_value
+mrb_ary_clear_m(mrb_state *mrb, mrb_value self)
+{
+  mrb_get_args(mrb, "");
+  return mrb_ary_clear(mrb, self);
+}
+
+static mrb_value
 mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
@@ -1113,12 +1118,6 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
 }
 
 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) {
@@ -1171,7 +1170,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
           val = tmp;
           goto str_join;
         }
-        tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+        tmp = mrb_check_array_type(mrb, val);
         if (!mrb_nil_p(tmp)) {
           val = tmp;
           goto ary_join;
@@ -1275,7 +1274,7 @@ mrb_init_array(mrb_state *mrb)
   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, "clear",           mrb_ary_clear_m,      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 */
index 30febdc..e4f5a30 100644 (file)
 
 struct backtrace_location {
   int lineno;
-  const char *filename;
   mrb_sym method_id;
+  const char *filename;
 };
 
-typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location*, void*);
+typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*, void*);
 
 static const mrb_data_type bt_type = { "Backtrace", mrb_free };
 
 static void
 each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
 {
-  ptrdiff_t i, j;
+  ptrdiff_t i;
 
   if (ciidx >= mrb->c->ciend - mrb->c->cibase)
     ciidx = 10; /* ciidx is broken... */
 
-  for (i=ciidx, j=0; i >= 0; i--,j++) {
+  for (i=ciidx; i >= 0; i--) {
     struct backtrace_location loc;
     mrb_callinfo *ci;
     mrb_irep *irep;
@@ -51,16 +51,17 @@ each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_fu
       pc = mrb->c->cibase[i].err;
     }
     else if (i+1 <= ciidx) {
+      if (!mrb->c->cibase[i + 1].pc) continue;
       pc = &mrb->c->cibase[i+1].pc[-1];
     }
     else {
       pc = pc0;
     }
-    loc.filename = mrb_debug_get_filename(irep, pc - irep->iseq);
-    loc.lineno = mrb_debug_get_line(irep, pc - irep->iseq);
 
+    loc.lineno = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
     if (loc.lineno == -1) continue;
 
+    loc.filename = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
     if (!loc.filename) {
       loc.filename = "(unknown)";
     }
@@ -79,8 +80,6 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace)
   mrb_int n;
   FILE *stream = stderr;
 
-  if (!mrb_array_p(backtrace)) return;
-
   n = RARRAY_LEN(backtrace) - 1;
   if (n == 0) return;
 
@@ -95,7 +94,7 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace)
 }
 
 static int
-packed_bt_len(struct backtrace_location *bt, int n)
+packed_bt_len(const struct backtrace_location *bt, int n)
 {
   int len = 0;
   int i;
@@ -112,7 +111,7 @@ static void
 print_packed_backtrace(mrb_state *mrb, mrb_value packed)
 {
   FILE *stream = stderr;
-  struct backtrace_location *bt;
+  const struct backtrace_location *bt;
   int n, i;
   int ai = mrb_gc_arena_save(mrb);
 
@@ -123,7 +122,7 @@ print_packed_backtrace(mrb_state *mrb, mrb_value packed)
   if (packed_bt_len(bt, n) == 0) return;
   fprintf(stream, "trace (most recent call last):\n");
   for (i = 0; i<n; i++) {
-    struct backtrace_location *entry = &bt[n-i-1];
+    const 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) {
@@ -171,7 +170,7 @@ mrb_print_backtrace(mrb_state *mrb)
 
 static void
 count_backtrace_i(mrb_state *mrb,
-                 struct backtrace_location *loc,
+                 const struct backtrace_location *loc,
                  void *data)
 {
   int *lenp = (int*)data;
@@ -182,7 +181,7 @@ count_backtrace_i(mrb_state *mrb,
 
 static void
 pack_backtrace_i(mrb_state *mrb,
-                 struct backtrace_location *loc,
+                 const struct backtrace_location *loc,
                  void *data)
 {
   struct backtrace_location **pptr = (struct backtrace_location**)data;
@@ -205,7 +204,6 @@ packed_backtrace(mrb_state *mrb)
   each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
   size = len * sizeof(struct backtrace_location);
   ptr = mrb_malloc(mrb, size);
-  if (ptr) 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);
@@ -229,7 +227,7 @@ mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
 mrb_value
 mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
 {
-  struct backtrace_location *bt;
+  const struct backtrace_location *bt;
   mrb_int n, i;
   int ai;
 
@@ -244,7 +242,7 @@ mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
   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];
+    const struct backtrace_location *entry = &bt[i];
     mrb_value btline;
 
     if (entry->filename == NULL) continue;
index c761f46..5c5ee9d 100644 (file)
@@ -65,7 +65,7 @@ mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb
   else {
     name = mrb_class_path(mrb, outer);
     if (mrb_nil_p(name)) {      /* unnamed outer class */
-      if (outer != mrb->object_class) {
+      if (outer != mrb->object_class && outer != c) {
         mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
                        mrb_obj_value(outer));
       }
@@ -93,7 +93,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
 
   if (o->c->tt == MRB_TT_SCLASS) return;
   sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
-  sc->flags |= MRB_FLAG_IS_INHERITED;
+  sc->flags |= MRB_FL_CLASS_IS_INHERITED;
   sc->mt = kh_init(mt, mrb);
   sc->iv = 0;
   if (o->tt == MRB_TT_CLASS) {
@@ -122,6 +122,19 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
   mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o));
 }
 
+static mrb_value
+class_name_str(mrb_state *mrb, struct RClass* c)
+{
+  mrb_value path = mrb_class_path(mrb, c);
+  if (mrb_nil_p(path)) {
+    path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#<Module:") :
+                                    mrb_str_new_lit(mrb, "#<Class:");
+    mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, c));
+    mrb_str_cat_lit(mrb, path, ">");
+  }
+  return path;
+}
+
 static struct RClass*
 class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
 {
@@ -275,7 +288,7 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
 
   if (!super)
     super = mrb->object_class;
-  super->flags |= MRB_FLAG_IS_INHERITED;
+  super->flags |= MRB_FL_CLASS_IS_INHERITED;
   s = mrb_obj_value(super);
   mc_clear_by_class(mrb, klass);
   mid = mrb_intern_lit(mrb, "inherited");
@@ -471,15 +484,11 @@ mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t
 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));
+    mrb_value str = mrb_sym2str(mrb, ci->mid);
+    mrb_raisef(mrb, E_NOTIMP_ERROR, "%S() function is unimplemented on this machine", str);
   }
 }
 
@@ -492,52 +501,34 @@ mrb_notimplement_m(mrb_state *mrb, mrb_value self)
   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;
-}
+#define CHECK_TYPE(mrb, val, t, c) do { \
+  if (mrb_type(val) != (t)) {\
+    mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_lit(mrb, c));\
+  }\
+} while (0)
 
 static mrb_value
 to_str(mrb_state *mrb, mrb_value val)
 {
-  return check_type(mrb, val, MRB_TT_STRING, "String", "to_str");
+  CHECK_TYPE(mrb, val, MRB_TT_STRING, "String");
+  return val;
 }
 
 static mrb_value
 to_ary(mrb_state *mrb, mrb_value val)
 {
-  return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+  CHECK_TYPE(mrb, val, MRB_TT_ARRAY, "Array");
+  return val;
 }
 
 static mrb_value
 to_hash(mrb_state *mrb, mrb_value val)
 {
-  return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash");
+  CHECK_TYPE(mrb, val, MRB_TT_HASH, "Hash");
+  return val;
 }
 
-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;
-  }
-}
+#define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss)
 
 MRB_API mrb_int
 mrb_get_argc(mrb_state *mrb)
@@ -1061,7 +1052,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
   while (m) {
     int superclass_seen = 0;
 
-    if (m->flags & MRB_FLAG_IS_PREPENDED)
+    if (m->flags & MRB_FL_CLASS_IS_PREPENDED)
       goto skip;
 
     if (klass_mt && klass_mt == m->mt)
@@ -1084,7 +1075,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
     }
 
     ic = include_class_new(mrb, m, ins_pos->super);
-    m->flags |= MRB_FLAG_IS_INHERITED;
+    m->flags |= MRB_FL_CLASS_IS_INHERITED;
     ins_pos->super = ic;
     mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
     mc_clear_by_class(mrb, ins_pos);
@@ -1111,15 +1102,15 @@ 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)) {
+  if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
     origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
-    origin->flags |= MRB_FLAG_IS_ORIGIN | MRB_FLAG_IS_INHERITED;
+    origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED;
     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;
+    c->flags |= MRB_FL_CLASS_IS_PREPENDED;
   }
   changed = include_module_at(mrb, c, c, m, 0);
   if (changed < 0) {
@@ -1196,7 +1187,7 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
     if (c->tt == MRB_TT_ICLASS) {
       mrb_ary_push(mrb, result, mrb_obj_value(c->c));
     }
-    else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) {
+    else if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
       mrb_ary_push(mrb, result, mrb_obj_value(c));
     }
     c = c->super;
@@ -1217,27 +1208,6 @@ mrb_mod_extend_object(mrb_state *mrb, mrb_value 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;
@@ -1250,45 +1220,6 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
   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);
 
@@ -1365,9 +1296,9 @@ mc_clear_by_class(mrb_state *mrb, struct RClass *c)
   struct mrb_cache_entry *mc = mrb->cache;
   int i;
 
-  if (c->flags & MRB_FLAG_IS_INHERITED) {
+  if (c->flags & MRB_FL_CLASS_IS_INHERITED) {
     mc_clear_all(mrb);
-    c->flags &= ~MRB_FLAG_IS_INHERITED;
+    c->flags &= ~MRB_FL_CLASS_IS_INHERITED;
     return;
   }
   for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
@@ -1381,9 +1312,9 @@ mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid)
   struct mrb_cache_entry *mc = mrb->cache;
   int i;
 
-  if (c->flags & MRB_FLAG_IS_INHERITED) {
+  if (c->flags & MRB_FL_CLASS_IS_INHERITED) {
     mc_clear_all(mrb);
-    c->flags &= ~MRB_FLAG_IS_INHERITED;
+    c->flags &= ~MRB_FL_CLASS_IS_INHERITED;
     return;
   }
   for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
@@ -1480,7 +1411,7 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
     mrb_str_cat_lit(mrb, str, "@");
     mrb_str_cat_str(mrb, str, name);
     sym = mrb_intern_str(mrb, str);
-    mrb_iv_check(mrb, sym);
+    mrb_iv_name_sym_check(mrb, sym);
     name = mrb_symbol_value(sym);
     p = mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name);
     MRB_METHOD_FROM_PROC(m, p);
@@ -1525,7 +1456,7 @@ mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
     mrb_str_cat_lit(mrb, str, "@");
     mrb_str_cat_str(mrb, str, name);
     sym = mrb_intern_str(mrb, str);
-    mrb_iv_check(mrb, sym);
+    mrb_iv_name_sym_check(mrb, sym);
     attr = mrb_symbol_value(sym);
 
     /* prepare method name (name=) */
@@ -1751,11 +1682,7 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
   }
   else if (mrb_symbol_p(path)) {
     /* toplevel class/module */
-    const char *str;
-    mrb_int len;
-
-    str = mrb_sym2name_len(mrb, mrb_symbol(path), &len);
-    return mrb_str_new(mrb, str, len);
+    return mrb_sym2str(mrb, mrb_symbol(path));
   }
   return mrb_str_dup(mrb, path);
 }
@@ -1763,10 +1690,10 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
 MRB_API struct RClass*
 mrb_class_real(struct RClass* cl)
 {
-  if (cl == 0)
-    return NULL;
+  if (cl == 0) return NULL;
   while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) {
     cl = cl->super;
+    if (cl == 0) return NULL;
   }
   return cl;
 }
@@ -1774,13 +1701,8 @@ mrb_class_real(struct RClass* 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_value name = class_name_str(mrb, c);
+  return RSTRING_PTR(name);
 }
 
 MRB_API const char*
@@ -1892,15 +1814,13 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const
  * show information on the thing we're attached to as well.
  */
 
-static mrb_value
+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:");
+    mrb_value str = mrb_str_new_lit(mrb, "#<Class:");
 
     if (class_ptr_p(v)) {
       mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
@@ -1911,34 +1831,7 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
     return mrb_str_cat_lit(mrb, str, ">");
   }
   else {
-    struct RClass *c;
-    mrb_value path;
-
-    str = mrb_str_new_capa(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;
-    }
+    return class_name_str(mrb, mrb_class_ptr(klass));
   }
 }
 
@@ -1953,8 +1846,8 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod)
   return mrb_nil_value();
 }
 
-static void
-undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
+void
+mrb_undef_method_id(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));
@@ -1970,7 +1863,7 @@ undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
 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_undef_method_id(mrb, c, mrb_intern_cstr(mrb, name));
 }
 
 MRB_API void
@@ -1988,297 +1881,26 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod)
 
   mrb_get_args(mrb, "*", &argv, &argc);
   while (argc--) {
-    undef_method(mrb, c, to_sym(mrb, *argv));
+    mrb_undef_method_id(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_method_t m;
-  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_METHOD_FROM_PROC(m, p);
-  mrb_define_method_raw(mrb, c, mid, m);
-  return mrb_symbol_value(mid);
-}
-
-static mrb_value
-top_define_method(mrb_state *mrb, mrb_value self)
-{
-  return mod_define_method(mrb, mrb_obj_value(mrb->object_class));
-}
-
-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)
+static mrb_bool
+const_name_p(mrb_state *mrb, const char *name, mrb_int len)
 {
-  if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) {
-    mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str);
-  }
+  return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1);
 }
 
 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));
+  mrb_int len;
+  const char *name = mrb_sym2name_len(mrb, id, &len);
+  if (!const_name_p(mrb, name, len)) {
+    mrb_name_error(mrb, id, "wrong constant name %S", mrb_sym2str(mrb, id));
   }
-  return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
 }
 
 static mrb_value
@@ -2289,7 +1911,10 @@ mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
 
   mrb_get_args(mrb, "n|b", &id, &inherit);
   check_const_name_sym(mrb, id);
-  return const_defined(mrb, mod, id, 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
@@ -2316,7 +1941,7 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
   }
 
   /* const get with class path string */
-  path = mrb_string_type(mrb, path);
+  path = mrb_ensure_string_type(mrb, path);
   ptr = RSTRING_PTR(path);
   len = RSTRING_LEN(path);
   off = 0;
@@ -2326,7 +1951,14 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
     end = (end == -1) ? len : end;
     id = mrb_intern(mrb, ptr+off, end-off);
     mod = mrb_const_get_sym(mrb, mod, id);
-    off = (end == len) ? end : end+2;
+    if (end == len)
+      off = end;
+    else {
+      off = end + 2;
+      if (off == len) {         /* trailing "::" */
+        mrb_name_error(mrb, id, "wrong constant name '%S'", path);
+      }
+    }
   }
 
   return mod;
@@ -2379,11 +2011,79 @@ mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
   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_s_constants(mrb_state *mrb, mrb_value mod)
+mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
 {
-  mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented");
-  return mrb_nil_value();       /* not reached */
+  mrb_sym id;
+
+  mrb_get_args(mrb, "n", &id);
+  return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id));
+}
+
+static mrb_value
+mod_define_method(mrb_state *mrb, mrb_value self)
+{
+  struct RClass *c = mrb_class_ptr(self);
+  struct RProc *p;
+  mrb_method_t m;
+  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_METHOD_FROM_PROC(m, p);
+  mrb_define_method_raw(mrb, c, mid, m);
+  return mrb_symbol_value(mid);
+}
+
+static mrb_value
+top_define_method(mrb_state *mrb, mrb_value self)
+{
+  return mod_define_method(mrb, mrb_obj_value(mrb->object_class));
 }
 
 static mrb_value
@@ -2398,7 +2098,7 @@ mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
   return mrb_bool_value(eqq);
 }
 
-MRB_API mrb_value
+static mrb_value
 mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
 {
   mrb_value *argv;
@@ -2439,8 +2139,6 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
 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);
-/* implementation of Module.nesting */
-mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value);
 
 static mrb_value
 inspect_main(mrb_state *mrb, mrb_value mod)
@@ -2489,8 +2187,8 @@ mrb_init_class(mrb_state *mrb)
   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, "__id__",                  mrb_obj_id_m,             MRB_ARGS_NONE()); /* 15.3.1.3.4  */
+  mrb_define_method(mrb, bob, "__send__",                mrb_f_send,               MRB_ARGS_ANY());  /* 15.3.1.3.5  */
   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));
@@ -2500,9 +2198,6 @@ mrb_init_class(mrb_state *mrb)
   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));
@@ -2511,18 +2206,12 @@ mrb_init_class(mrb_state *mrb)
   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());
@@ -2533,14 +2222,11 @@ mrb_init_class(mrb_state *mrb)
   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, "method_defined?",         mrb_mod_method_defined,   MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */
   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_define_class_method(mrb, mod, "nesting",           mrb_mod_s_nesting,        MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */
 
   mrb_undef_method(mrb, cls, "append_features");
   mrb_undef_method(mrb, cls, "extend_object");
index d79a65a..5bffefd 100644 (file)
 #include <mruby/proc.h>
 
 #ifndef MRB_DISABLE_STDIO
-static int
-print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre)
+static void
+print_r(mrb_state *mrb, mrb_irep *irep, size_t n)
 {
   size_t i;
 
-  if (n == 0) return 0;
+  if (n == 0) return;
 
   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;
+      printf(" R%d:%s", (int)n, mrb_sym2name(mrb, sym));
+      break;
     }
   }
-  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)
+print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a)
 {
-  int pre = 0;
+  if (!irep->lv || a >= irep->nlocals || a == 0) {
+    printf("\n");
+    return;
+  }
+  printf("\t;");
+  print_r(mrb, irep, a);
+  printf("\n");
+}
 
-  if (!irep->lv
-      || ((!(r & RA) || GETARG_A(c) >= irep->nlocals)
-       && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) {
+static void
+print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b)
+{
+  if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) {
     printf("\n");
     return;
   }
-  printf("\t; ");
-  if (r & RA) {
-    pre = print_r(mrb, irep, GETARG_A(c), 0);
+  printf("\t;");
+  if (a > 0) print_r(mrb, irep, a);
+  if (b > 0) print_r(mrb, irep, b);
+  printf("\n");
+}
+
+static void
+print_header(mrb_state *mrb, mrb_irep *irep, ptrdiff_t i)
+{
+  int32_t line;
+
+  line = mrb_debug_get_line(mrb, irep, i);
+  if (line < 0) {
+    printf("      ");
   }
-  if (r & RB) {
-    print_r(mrb, irep, GETARG_B(c), pre);
+  else {
+    printf("%5d ", line);
   }
-  printf("\n");
+
+  printf("%03d ", (int)i);
 }
-#endif
+
+#define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn
 
 static void
 codedump(mrb_state *mrb, mrb_irep *irep)
 {
-#ifndef MRB_DISABLE_STDIO
-  int i;
   int ai;
-  mrb_code c;
+  mrb_code *pc, *pcend;
+  mrb_code ins;
   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);
+  printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d iseq=%d\n", (void*)irep,
+         irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen);
+
+  if (irep->lv) {
+    int i;
+
+    printf("local variable names:\n");
+    for (i = 1; i < irep->nlocals; ++i) {
+      char const *s = mrb_sym2name(mrb, irep->lv[i - 1].name);
+      int n = irep->lv[i - 1].r ? irep->lv[i - 1].r : i;
+      printf("  R%d:%s\n", n, s ? s : "");
+    }
+  }
+
+  pc = irep->iseq;
+  pcend = pc + irep->ilen;
+  while (pc < pcend) {
+    ptrdiff_t i;
+    uint32_t a;
+    uint16_t b;
+    uint8_t c;
 
-  for (i = 0; i < (int)irep->ilen; i++) {
     ai = mrb_gc_arena_save(mrb);
 
-    next_file = mrb_debug_get_filename(irep, i);
+    i = pc - irep->iseq;
+    next_file = mrb_debug_get_filename(mrb, 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:
+    print_header(mrb, irep, i);
+    ins = READ_B();
+    switch (ins) {
+    CASE(OP_NOP, Z):
       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);
+    CASE(OP_MOVE, BB):
+      printf("OP_MOVE\tR%d\tR%d\t", a, b);
+      print_lv_ab(mrb, irep, a, b);
       break;
-    case OP_LOADL:
+    CASE(OP_LOADL, BB):
       {
-        mrb_value v = irep->pool[GETARG_Bx(c)];
+        mrb_value v = irep->pool[b];
         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));
+        printf("OP_LOADL\tR%d\tL(%d)\t; %s", a, b, 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 (%d)\n", i+GETARG_sBx(c), GETARG_sBx(c));
-      break;
-    case OP_JMPIF:
-      printf("OP_JMPIF\tR%d\t%03d (%d)\n", GETARG_A(c), i+GETARG_sBx(c), GETARG_sBx(c));
-      break;
-    case OP_JMPNOT:
-      printf("OP_JMPNOT\tR%d\t%03d (%d)\n", GETARG_A(c), i+GETARG_sBx(c), 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_CALL:
-      printf("OP_CALL\tR%d\n", GETARG_A(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);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADI, BB):
+      printf("OP_LOADI\tR%d\t%d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADINEG, BB):
+      printf("OP_LOADI\tR%d\t-%d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADI__1, B):
+      printf("OP_LOADI__1\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADI_0, B): goto L_LOADI;
+    CASE(OP_LOADI_1, B): goto L_LOADI;
+    CASE(OP_LOADI_2, B): goto L_LOADI;
+    CASE(OP_LOADI_3, B): goto L_LOADI;
+    CASE(OP_LOADI_4, B): goto L_LOADI;
+    CASE(OP_LOADI_5, B): goto L_LOADI;
+    CASE(OP_LOADI_6, B): goto L_LOADI;
+    CASE(OP_LOADI_7, B):
+    L_LOADI:
+      printf("OP_LOADI_%d\tR%d\t\t", ins-(int)OP_LOADI_0, a);
+      print_lv_a(mrb, irep, a);
       break;
-
-    case OP_ENTER:
+    CASE(OP_LOADSYM, BB):
+      printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADNIL, B):
+      printf("OP_LOADNIL\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADSELF, B):
+      printf("OP_LOADSELF\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADT, B):
+      printf("OP_LOADT\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LOADF, B):
+      printf("OP_LOADF\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_GETGV, BB):
+      printf("OP_GETGV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SETGV, BB):
+      printf("OP_SETGV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_GETSV, BB):
+      printf("OP_GETSV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SETSV, BB):
+      printf("OP_SETSV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_GETCONST, BB):
+      printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SETCONST, BB):
+      printf("OP_SETCONST\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_GETMCNST, BB):
+      printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SETMCNST, BB):
+      printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym2name(mrb, irep->syms[b]), a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_GETIV, BB):
+      printf("OP_GETIV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SETIV, BB):
+      printf("OP_SETIV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_GETUPVAR, BBB):
+      printf("OP_GETUPVAR\tR%d\t%d\t%d", a, b, c);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SETUPVAR, BBB):
+      printf("OP_SETUPVAR\tR%d\t%d\t%d", a, b, c);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_GETCV, BB):
+      printf("OP_GETCV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SETCV, BB):
+      printf("OP_SETCV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_JMP, S):
+      printf("OP_JMP\t\t%03d\n", a);
+      break;
+    CASE(OP_JMPIF, BS):
+      printf("OP_JMPIF\tR%d\t%03d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_JMPNOT, BS):
+      printf("OP_JMPNOT\tR%d\t%03d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_JMPNIL, BS):
+      printf("OP_JMPNIL\tR%d\t%03d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_SENDV, BB):
+      printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b]));
+      break;
+    CASE(OP_SENDVB, BB):
+      printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b]));
+      break;
+    CASE(OP_SEND, BBB):
+      printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c);
+      break;
+    CASE(OP_SENDB, BBB):
+      printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c);
+      break;
+    CASE(OP_CALL, Z):
+      printf("OP_CALL\n");
+      break;
+    CASE(OP_SUPER, BB):
+      printf("OP_SUPER\tR%d\t%d\n", a, b);
+      break;
+    CASE(OP_ARGARY, BS):
+      printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a,
+             (b>>11)&0x3f,
+             (b>>10)&0x1,
+             (b>>5)&0x1f,
+             (b>>4)&0x1,
+             (b>>0)&0xf);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_ENTER, W):
       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);
+             (a>>18)&0x1f,
+             (a>>13)&0x1f,
+             (a>>12)&0x1,
+             (a>>7)&0x1f,
+             (a>>2)&0x1f,
+             (a>>1)&0x1,
+             a & 0x1);
       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);
+    CASE(OP_KEY_P, BB):
+      printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_KEYEND, Z):
+      printf("OP_KEYEND\n");
       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);
+    CASE(OP_KARG, BB):
+      printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_RETURN, B):
+      printf("OP_RETURN\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
       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\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_LE:
-      printf("OP_LE\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_GT:
-      printf("OP_GT\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_GE:
-      printf("OP_GE\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_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));
+    CASE(OP_RETURN_BLK, B):
+      printf("OP_RETURN_BLK\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
       break;
-
-    case OP_STOP:
-      printf("OP_STOP\n");
+    CASE(OP_BREAK, B):
+      printf("OP_BREAK\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_BLKPUSH, BS):
+      printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a,
+             (b>>11)&0x3f,
+             (b>>10)&0x1,
+             (b>>5)&0x1f,
+             (b>>4)&0x1,
+             (b>>0)&0xf);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_LAMBDA, BB):
+      printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+      break;
+    CASE(OP_BLOCK, BB):
+      printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+      break;
+    CASE(OP_METHOD, BB):
+      printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+      break;
+    CASE(OP_RANGE_INC, B):
+      printf("OP_RANGE_INC\tR%d\n", a);
+      break;
+    CASE(OP_RANGE_EXC, B):
+      printf("OP_RANGE_EXC\tR%d\n", a);
+      break;
+    CASE(OP_DEF, BB):
+      printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b]));
+      break;
+    CASE(OP_UNDEF, B):
+      printf("OP_UNDEF\t:%s\n", mrb_sym2name(mrb, irep->syms[a]));
+      break;
+    CASE(OP_ALIAS, BB):
+      printf("OP_ALIAS\t:%s\t%s\n", mrb_sym2name(mrb, irep->syms[a]), mrb_sym2name(mrb, irep->syms[b]));
+      break;
+    CASE(OP_ADD, B):
+      printf("OP_ADD\tR%d\t\n", a);
+      break;
+    CASE(OP_ADDI, BB):
+      printf("OP_ADDI\tR%d\t%d\n", a, b);
+      break;
+    CASE(OP_SUB, B):
+      printf("OP_SUB\tR%d\t\n", a);
+      break;
+    CASE(OP_SUBI, BB):
+      printf("OP_SUBI\tR%d\t%d\n", a, b);
+      break;
+    CASE(OP_MUL, B):
+      printf("OP_MUL\tR%d\t\n", a);
+      break;
+    CASE(OP_DIV, B):
+      printf("OP_DIV\tR%d\t\n", a);
+      break;
+    CASE(OP_LT, B):
+      printf("OP_LT\t\tR%d\t\n", a);
+      break;
+    CASE(OP_LE, B):
+      printf("OP_LE\t\tR%d\t\n", a);
+      break;
+    CASE(OP_GT, B):
+      printf("OP_GT\t\tR%d\t\n", a);
+      break;
+    CASE(OP_GE, B):
+      printf("OP_GE\t\tR%d\t\n", a);
+      break;
+    CASE(OP_EQ, B):
+      printf("OP_EQ\t\tR%d\t\n", a);
+      break;
+    CASE(OP_ARRAY, BB):
+      printf("OP_ARRAY\tR%d\t%d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_ARRAY2, BBB):
+      printf("OP_ARRAY\tR%d\tR%d\t%d\t", a, b, c);
+      print_lv_ab(mrb, irep, a, b);
       break;
-    case OP_ARYCAT:
-      printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
-      print_lv(mrb, irep, c, RAB);
+    CASE(OP_ARYCAT, B):
+      printf("OP_ARYCAT\tR%d\t", a);
+      print_lv_a(mrb, irep, a);
       break;
-    case OP_ARYPUSH:
-      printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
-      print_lv(mrb, irep, c, RAB);
+    CASE(OP_ARYPUSH, B):
+      printf("OP_ARYPUSH\tR%d\t", a);
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_ARYDUP, B):
+      printf("OP_ARYDUP\tR%d\t", a);
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_AREF, BBB):
+      printf("OP_AREF\tR%d\tR%d\t%d", a, b, c);
+      print_lv_ab(mrb, irep, a, b);
       break;
-    case OP_STRING:
+    CASE(OP_ASET, BBB):
+      printf("OP_ASET\tR%d\tR%d\t%d", a, b, c);
+      print_lv_ab(mrb, irep, a, b);
+      break;
+    CASE(OP_APOST, BBB):
+      printf("OP_APOST\tR%d\t%d\t%d", a, b, c);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_INTERN, B):
+      printf("OP_INTERN\tR%d", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_STRING, BB):
       {
-        mrb_value v = irep->pool[GETARG_Bx(c)];
+        mrb_value v = irep->pool[b];
         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));
+        printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s));
       }
-      print_lv(mrb, irep, c, RA);
+      print_lv_a(mrb, irep, a);
       break;
-    case OP_STRCAT:
-      printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
-      print_lv(mrb, irep, c, RAB);
+    CASE(OP_STRCAT, B):
+      printf("OP_STRCAT\tR%d\t", a);
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_HASH, BB):
+      printf("OP_HASH\tR%d\t%d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_HASHADD, BB):
+      printf("OP_HASHADD\tR%d\t%d\t", a, b);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_HASHCAT, B):
+      printf("OP_HASHCAT\tR%d\t", a);
+      print_lv_a(mrb, irep, a);
       break;
 
-    case OP_OCLASS:
-      printf("OP_OCLASS\tR%d\t\t", GETARG_A(c));
-      print_lv(mrb, irep, c, RA);
+    CASE(OP_OCLASS, B):
+      printf("OP_OCLASS\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_CLASS, BB):
+      printf("OP_CLASS\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
       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);
+    CASE(OP_MODULE, BB):
+      printf("OP_MODULE\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+      print_lv_a(mrb, irep, a);
       break;
-    case OP_EXEC:
-      printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1);
-      print_lv(mrb, irep, c, RA);
+    CASE(OP_EXEC, BB):
+      printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, irep->reps[b]);
+      print_lv_a(mrb, irep, a);
       break;
-    case OP_SCLASS:
-      printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
-      print_lv(mrb, irep, c, RAB);
+    CASE(OP_SCLASS, B):
+      printf("OP_SCLASS\tR%d\t", a);
+      print_lv_a(mrb, irep, a);
       break;
-    case OP_TCLASS:
-      printf("OP_TCLASS\tR%d\t\t", GETARG_A(c));
-      print_lv(mrb, irep, c, RA);
+    CASE(OP_TCLASS, B):
+      printf("OP_TCLASS\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
       break;
-    case OP_ERR:
+    CASE(OP_ERR, B):
       {
-        mrb_value v = irep->pool[GETARG_Bx(c)];
+        mrb_value v = irep->pool[a];
         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);
+    CASE(OP_EPUSH, B):
+      printf("OP_EPUSH\t\t:I(%d:%p)\n", a, irep->reps[a]);
       break;
-    case OP_ONERR:
-      printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
+    CASE(OP_ONERR, S):
+      printf("OP_ONERR\t%03d\n", a);
+      break;
+    CASE(OP_EXCEPT, B):
+      printf("OP_EXCEPT\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_RESCUE, BB):
+      printf("OP_RESCUE\tR%d\tR%d", a, b);
+      print_lv_ab(mrb, irep, a, b);
+      break;
+    CASE(OP_RAISE, B):
+      printf("OP_RAISE\tR%d\t\t", a);
+      print_lv_a(mrb, irep, a);
+      break;
+    CASE(OP_POPERR, B):
+      printf("OP_POPERR\t%d\t\t\n", a);
+      break;
+    CASE(OP_EPOP, B):
+      printf("OP_EPOP\t%d\n", a);
       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;
-        }
-      }
+    CASE(OP_DEBUG, BBB):
+      printf("OP_DEBUG\t%d\t%d\t%d\n", a, b, c);
       break;
-    case OP_RAISE:
-      printf("OP_RAISE\tR%d\t\t", GETARG_A(c));
-      print_lv(mrb, irep, c, RA);
+
+    CASE(OP_STOP, Z):
+      printf("OP_STOP\n");
       break;
-    case OP_POPERR:
-      printf("OP_POPERR\t%d\t\t\n", GETARG_A(c));
+
+    CASE(OP_EXT1, Z):
+      ins = READ_B();
+      printf("OP_EXT1\n");
+      print_header(mrb, irep, pc-irep->iseq-2);
+      switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+      }
       break;
-    case OP_EPOP:
-      printf("OP_EPOP\t%d\n", GETARG_A(c));
+    CASE(OP_EXT2, Z):
+      ins = READ_B();
+      printf("OP_EXT2\n");
+      print_header(mrb, irep, pc-irep->iseq-2);
+      switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+      }
+      break;
+    CASE(OP_EXT3, Z):
+      ins = READ_B();
+      printf("OP_EXT3\n");
+      print_header(mrb, irep, pc-irep->iseq-2);
+      switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+      }
       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));
+      printf("OP_unknown (0x%x)\n", ins);
       break;
     }
     mrb_gc_arena_restore(mrb, ai);
   }
   printf("\n");
-#endif
 }
 
 static void
@@ -469,9 +538,12 @@ codedump_recur(mrb_state *mrb, mrb_irep *irep)
     codedump_recur(mrb, irep->reps[i]);
   }
 }
+#endif
 
 void
 mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
 {
+#ifndef MRB_DISABLE_STDIO
   codedump_recur(mrb, proc->body.irep);
+#endif
 }
index e55f11d..0dc02a1 100644 (file)
@@ -51,25 +51,25 @@ select_line_type(const uint16_t *lines, size_t lines_len)
 }
 
 MRB_API char const*
-mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc)
+mrb_debug_get_filename(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
 {
   if (irep && pc >= 0 && pc < irep->ilen) {
     mrb_irep_debug_info_file* f = NULL;
-    if (!irep->debug_info) { return irep->filename; }
+    if (!irep->debug_info) return NULL;
     else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
-      return f->filename;
+      return mrb_sym2name_len(mrb, f->filename_sym, NULL);
     }
   }
   return NULL;
 }
 
 MRB_API int32_t
-mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc)
+mrb_debug_get_line(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
 {
   if (irep && pc >= 0 && pc < irep->ilen) {
     mrb_irep_debug_info_file* f = NULL;
     if (!irep->debug_info) {
-      return irep->lines? irep->lines[pc] : -1;
+      return -1;
     }
     else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
       switch (f->line_type) {
@@ -122,82 +122,79 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
 }
 
 MRB_API mrb_irep_debug_info_file*
-mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep,
+mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d,
+                           const char *filename, uint16_t *lines,
                            uint32_t start_pos, uint32_t end_pos)
 {
-  mrb_irep_debug_info *info;
-  mrb_irep_debug_info_file *ret;
+  mrb_irep_debug_info_file *f;
   uint32_t file_pc_count;
   size_t fn_len;
-  mrb_int len;
   uint32_t i;
 
-  if (!irep->debug_info) { return NULL; }
+  if (!d) return NULL;
+  if (start_pos == end_pos) return NULL;
 
-  mrb_assert(irep->filename);
-  mrb_assert(irep->lines);
+  mrb_assert(filename);
+  mrb_assert(lines);
 
-  info = irep->debug_info;
-
-  if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 0) {
-    return NULL;
+  if (d->flen > 0) {
+    const char *fn = mrb_sym2name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL);
+    if (strcmp(filename, fn) == 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))
+  f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f));
+  d->files = (mrb_irep_debug_info_file**)(
+          d->files
+          ? mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1))
           : mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*)));
-  info->files[info->flen++] = ret;
+  d->files[d->flen++] = f;
 
   file_pc_count = end_pos - start_pos;
 
-  ret->start_pos = start_pos;
-  info->pc_count = end_pos;
+  f->start_pos = start_pos;
+  d->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);
+  fn_len = strlen(filename);
+  f->filename_sym = mrb_intern(mrb, filename, fn_len);
 
-  ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos);
-  ret->lines.ptr = NULL;
+  f->line_type = select_line_type(lines + start_pos, end_pos - start_pos);
+  f->lines.ptr = NULL;
 
-  switch (ret->line_type) {
+  switch (f->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);
+      f->line_entry_count = file_pc_count;
+      f->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];
+        f->lines.ary[i] = 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;
+      f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
+      f->line_entry_count = 0;
       for (i = 0; i < file_pc_count; ++i) {
-        if (irep->lines[start_pos + i] == prev_line) { continue; }
+        if (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));
+        f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
+            mrb, f->lines.flat_map,
+            sizeof(mrb_irep_debug_info_line) * (f->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;
+        m.line = lines[start_pos + i];
+        f->lines.flat_map[f->line_entry_count] = m;
 
         /* update */
-        ++ret->line_entry_count;
-        prev_line = irep->lines[start_pos + i];
+        ++f->line_entry_count;
+        prev_line = lines[start_pos + i];
       }
     } break;
 
     default: mrb_assert(0); break;
   }
 
-  return ret;
+  return f;
 }
 
 MRB_API void
index df1e171..f1e167e 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <string.h>
 #include <limits.h>
+#include <math.h>
 #include <mruby/dump.h>
 #include <mruby/string.h>
 #include <mruby/irep.h>
@@ -17,9 +18,9 @@
 
 #ifndef MRB_WITHOUT_FLOAT
 #ifdef MRB_USE_FLOAT
-#define MRB_FLOAT_FMT "%.8e"
+#define MRB_FLOAT_FMT "%.9g"
 #else
-#define MRB_FLOAT_FMT "%.16e"
+#define MRB_FLOAT_FMT "%.17g"
 #endif
 #endif
 
@@ -81,34 +82,27 @@ static ptrdiff_t
 write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
 {
   uint8_t *cur = buf;
-  int 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;
-  }
+  memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code));
+  cur += irep->ilen * sizeof(mrb_code);
 
   return cur - buf;
 }
 
+#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+float_to_str(mrb_state *mrb, mrb_value flt)
+{
+  mrb_float f = mrb_float(flt);
+
+  if (isinf(f)) {
+    return f < 0 ? mrb_str_new_lit(mrb, "I") : mrb_str_new_lit(mrb, "i");
+  }
+  return  mrb_float_to_str(mrb, flt, MRB_FLOAT_FMT);
+}
+#endif
 
 static size_t
 get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
@@ -135,7 +129,7 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
 
 #ifndef MRB_WITHOUT_FLOAT
     case MRB_TT_FLOAT:
-      str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
+      str = float_to_str(mrb, irep->pool[pool_no]);
       {
         mrb_int len = RSTRING_LEN(str);
         mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
@@ -184,7 +178,7 @@ write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
 #ifndef MRB_WITHOUT_FLOAT
     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);
+      str = float_to_str(mrb, irep->pool[pool_no]);
       break;
 #endif
 
@@ -372,118 +366,6 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p,
   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;
-  int 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 rlen, size = 0;
-  int i;
-
-  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)
 {
@@ -857,26 +739,26 @@ write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8
 }
 
 static mrb_bool
-is_debug_info_defined(mrb_irep *irep)
+debug_info_defined_p(mrb_irep *irep)
 {
   int i;
 
   if (!irep->debug_info) return FALSE;
   for (i=0; i<irep->rlen; i++) {
-    if (!is_debug_info_defined(irep->reps[i])) return FALSE;
+    if (!debug_info_defined_p(irep->reps[i])) return FALSE;
   }
   return TRUE;
 }
 
 static mrb_bool
-is_lv_defined(mrb_irep *irep)
+lv_defined_p(mrb_irep *irep)
 {
   int i;
 
   if (irep->lv) { return TRUE; }
 
   for (i = 0; i < irep->rlen; ++i) {
-    if (is_lv_defined(irep->reps[i])) { return TRUE; }
+    if (lv_defined_p(irep->reps[i])) { return TRUE; }
   }
 
   return FALSE;
@@ -905,7 +787,7 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
   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_bool const debug_info_defined = debug_info_defined_p(irep), lv_defined = lv_defined_p(irep);
   mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0;
   mrb_sym *filenames = NULL; uint16_t filenames_len = 0;
 
@@ -930,10 +812,6 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
 
       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) {
@@ -961,12 +839,9 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
   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;
+      if (result != MRB_DUMP_OK) {
+        goto error_exit;
+      }
     }
     cur += section_lineno_size;
   }
index adb815b..1e94451 100644 (file)
@@ -5,10 +5,26 @@
 */
 
 #include <mruby.h>
+#include <mruby/proc.h>
+
+/* internal method `__update_hash(oldhash, index, itemhash)` */
+static mrb_value
+enum_update_hash(mrb_state *mrb, mrb_value self)
+{
+  mrb_int hash;
+  mrb_int index;
+  mrb_int hv;
+
+  mrb_get_args(mrb, "iii", &hash, &index, &hv);
+  hash ^= ((uint32_t)hv << (index % 16));
+
+  return mrb_fixnum_value(hash);
+}
 
 void
 mrb_init_enumerable(mrb_state *mrb)
 {
-  mrb_define_module(mrb, "Enumerable");  /* 15.3.2 */
+  struct RClass *enumerable;
+  enumerable = mrb_define_module(mrb, "Enumerable");  /* 15.3.2 */
+  mrb_define_module_function(mrb, enumerable, "__update_hash", enum_update_hash, MRB_ARGS_REQ(1));
 }
-
index 5445b51..e69812d 100644 (file)
@@ -28,7 +28,7 @@ mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
 MRB_API mrb_value
 mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str)
 {
-  str = mrb_str_to_str(mrb, str);
+  mrb_to_str(mrb, str);
   return mrb_obj_new(mrb, c, 1, &str);
 }
 
@@ -208,8 +208,8 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
     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, err - irep->iseq);
-      char const* file = mrb_debug_get_filename(irep, err - irep->iseq);
+      int32_t const line = mrb_debug_get_line(mrb, irep, err - irep->iseq);
+      char const* file = mrb_debug_get_filename(mrb, irep, 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));
@@ -233,7 +233,7 @@ mrb_exc_set(mrb_state *mrb, mrb_value exc)
         (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) {
       mrb->gc.arena_idx--;
     }
-    if (!mrb->gc.out_of_memory) {
+    if (!mrb->gc.out_of_memory && !MRB_FROZEN_P(mrb->exc)) {
       exc_debug_info(mrb, mrb->exc);
       mrb_keep_backtrace(mrb, exc);
     }
@@ -376,6 +376,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
   str = mrb_vformat(mrb, fmt, ap);
   fputs("warning: ", stderr);
   fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
+  putc('\n', stderr);
   va_end(ap);
 #endif
 }
index 1b8d44a..12d948a 100644 (file)
@@ -75,6 +75,7 @@ mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
       if (mrb_nil_p(name)) {
         name = mrb_inspect(mrb, name);
         mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name);
+        /* not reached */
       }
       /* fall through */
     case MRB_TT_STRING:
diff --git a/third-party/mruby/src/ext/.gitkeep b/third-party/mruby/src/ext/.gitkeep
deleted file mode 100644 (file)
index e69de29..0000000
index f8a8f79..14c74ef 100644 (file)
@@ -1,3 +1,5 @@
+#ifndef MRB_WITHOUT_FLOAT
+#if defined(MRB_DISABLE_STDIO) || defined(_WIN32) || defined(_WIN64)
 /*
 
 Most code in this file originates from musl (src/stdio/vfprintf.c)
@@ -36,7 +38,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <mruby.h>
 #include <mruby/string.h>
 
-#ifndef MRB_WITHOUT_FLOAT
 struct fmt_args {
   mrb_state *mrb;
   mrb_value str;
@@ -371,4 +372,17 @@ mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
   }
   return f.str;
 }
+#else   /* MRB_DISABLE_STDIO */
+#include <mruby.h>
+#include <stdio.h>
+
+mrb_value
+mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
+{
+  char buf[25];
+
+  snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
+  return mrb_str_new_cstr(mrb, buf);
+}
+#endif  /* MRB_DISABLE_STDIO */
 #endif
index 8e20727..ec52787 100644 (file)
@@ -274,9 +274,29 @@ mrb_free(mrb_state *mrb, void *p)
   (mrb->allocf)(mrb, p, 0, mrb->allocf_ud);
 }
 
+static mrb_bool
+heap_p(mrb_gc *gc, struct RBasic *object)
+{
+  mrb_heap_page* page;
+
+  page = gc->heaps;
+  while (page) {
+    RVALUE *p;
+
+    p = objects(page);
+    if (&p[0].as.basic <= object && object <= &p[MRB_HEAP_PAGE_SIZE].as.basic) {
+      return TRUE;
+    }
+    page = page->next;
+  }
+  return FALSE;
+}
+
 MRB_API mrb_bool
 mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) {
-  return is_dead(&mrb->gc, object);
+  mrb_gc *gc = &mrb->gc;
+  if (!heap_p(gc, object)) return TRUE;
+  return is_dead(gc, object);
 }
 
 static void
@@ -548,21 +568,39 @@ add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
   gc->gray_list = obj;
 }
 
+static int
+ci_nregs(mrb_callinfo *ci)
+{
+  struct RProc *p = ci->proc;
+  int n = 0;
+
+  if (!p) {
+    if (ci->argc < 0) return 3;
+    return ci->argc+2;
+  }
+  if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
+    n = p->body.irep->nregs;
+  }
+  if (ci->argc < 0) {
+    if (n < 3) n = 3; /* self + args + blk */
+  }
+  if (ci->argc > n) {
+    n = ci->argc + 2; /* self + blk */
+  }
+  return n;
+}
+
 static void
 mark_context_stack(mrb_state *mrb, struct mrb_context *c)
 {
   size_t i;
   size_t e;
   mrb_value nil;
-  mrb_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;
+    e += ci_nregs(c->ci);
   }
   if (c->stbase + e > c->stend) e = c->stend - c->stbase;
   for (i=0; i<e; i++) {
@@ -622,7 +660,7 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
   case MRB_TT_ICLASS:
     {
       struct RClass *c = (struct RClass*)obj;
-      if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN))
+      if (MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_ORIGIN))
         mrb_gc_mark_mt(mrb, c);
       mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
     }
@@ -701,14 +739,7 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
     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);
-      }
-    }
+    mrb_gc_mark_range(mrb, (struct RRange*)obj);
     break;
 
   default:
@@ -761,7 +792,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
     mrb_gc_free_iv(mrb, (struct RObject*)obj);
     break;
   case MRB_TT_ICLASS:
-    if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN))
+    if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN))
       mrb_gc_free_mt(mrb, (struct RClass*)obj);
     break;
   case MRB_TT_ENV:
@@ -770,7 +801,8 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
 
       if (MRB_ENV_STACK_SHARED_P(e)) {
         /* cannot be freed */
-        return;
+        e->stack = NULL;
+        break;
       }
       mrb_free(mrb, e->stack);
       e->stack = NULL;
@@ -782,13 +814,13 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
       struct mrb_context *c = ((struct RFiber*)obj)->cxt;
 
       if (c && c != mrb->root_c) {
-        mrb_callinfo *ci = c->ci;
-        mrb_callinfo *ce = c->cibase;
+        if (!end && c->status != MRB_FIBER_TERMINATED) {
+          mrb_callinfo *ci = c->ci;
+          mrb_callinfo *ce = c->cibase;
 
-        if (!end) {
           while (ce <= ci) {
             struct REnv *e = ci->env;
-            if (e && !is_dead(&mrb->gc, e) &&
+            if (e && !mrb_object_dead_p(mrb, (struct RBasic*)e) &&
                 e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
               mrb_env_unshare(mrb, e);
             }
@@ -831,7 +863,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
     break;
 
   case MRB_TT_RANGE:
-    mrb_free(mrb, ((struct RRange*)obj)->edges);
+    mrb_gc_free_range(mrb, ((struct RRange*)obj));
     break;
 
   case MRB_TT_DATA:
@@ -938,7 +970,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
     break;
 
   case MRB_TT_ENV:
-    children += (int)obj->flags;
+    children += MRB_ENV_STACK_LEN(obj);
     break;
 
   case MRB_TT_FIBER:
@@ -947,10 +979,14 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
       size_t i;
       mrb_callinfo *ci;
 
-      if (!c) break;
+      if (!c || c->status == MRB_FIBER_TERMINATED) break;
+
       /* mark stack */
       i = c->stack - c->stbase;
-      if (c->ci) i += c->ci->nregs;
+
+      if (c->ci) {
+        i += ci_nregs(c->ci);
+      }
       if (c->stbase + i > c->stend) i = c->stend - c->stbase;
       children += i;
 
@@ -1537,7 +1573,7 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo
       mrb->jmp = &c_jmp;
       gc_each_objects(mrb, &mrb->gc, callback, data);
       mrb->jmp = prev_jmp;
-      mrb->gc.iterating = iterating; 
+      mrb->gc.iterating = iterating;
    } MRB_CATCH(&c_jmp) {
       mrb->gc.iterating = iterating;
       mrb->jmp = prev_jmp;
index d587069..fd963c3 100644 (file)
@@ -8,7 +8,6 @@
 #include <mruby/array.h>
 #include <mruby/class.h>
 #include <mruby/hash.h>
-#include <mruby/khash.h>
 #include <mruby/string.h>
 #include <mruby/variable.h>
 
 mrb_int mrb_float_id(mrb_float f);
 #endif
 
-static inline khint_t
-mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
+#ifndef MRB_HT_INIT_SIZE
+#define MRB_HT_INIT_SIZE 4
+#endif
+#define HT_SEG_INCREASE_RATIO 6 / 5
+
+struct segkv {
+  mrb_value key;
+  mrb_value val;
+};
+
+typedef struct segment {
+  uint16_t size;
+  struct segment *next;
+  struct segkv e[];
+} segment;
+
+typedef struct segindex {
+  size_t size;
+  size_t capa;
+  struct segkv *table[];
+} segindex;
+
+/* hash table structure */
+typedef struct htable {
+  segment *rootseg;
+  segment *lastseg;
+  mrb_int size;
+  uint16_t last_len;
+  segindex *index;
+} htable;
+
+static /* inline */ size_t
+ht_hash_func(mrb_state *mrb, htable *t, mrb_value key)
 {
-  enum mrb_vtype t = mrb_type(key);
+  enum mrb_vtype tt = mrb_type(key);
   mrb_value hv;
-  khint_t h;
+  size_t h;
+  segindex *index = t->index;
+  size_t capa = index ? index->capa : 0;
 
-  switch (t) {
+  switch (tt) {
   case MRB_TT_STRING:
     h = mrb_str_hash(mrb, key);
     break;
@@ -36,23 +68,26 @@ mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
 #ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
 #endif
-    h = (khint_t)mrb_obj_id(key);
+    h = (size_t)mrb_obj_id(key);
     break;
 
   default:
     hv = mrb_funcall(mrb, key, "hash", 0);
-    h = (khint_t)t ^ (khint_t)mrb_fixnum(hv);
+    h = (size_t)t ^ (size_t)mrb_fixnum(hv);
     break;
   }
-  return kh_int_hash_func(mrb, h);
+  if (index && (index != t->index || capa != index->capa)) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified");
+  }
+  return ((h)^((h)<<2)^((h)>>2));
 }
 
-static inline khint_t
-mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
+static inline mrb_bool
+ht_hash_equal(mrb_state *mrb, htable *t, mrb_value a, mrb_value b)
 {
-  enum mrb_vtype t = mrb_type(a);
+  enum mrb_vtype tt = mrb_type(a);
 
-  switch (t) {
+  switch (tt) {
   case MRB_TT_STRING:
     return mrb_str_equal(mrb, a, b);
 
@@ -85,16 +120,460 @@ mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
 #endif
 
   default:
-    return mrb_eql(mrb, a, b);
+    {
+      segindex *index = t->index;
+      size_t capa = index ? index->capa : 0;
+      mrb_bool eql = mrb_eql(mrb, a, b);
+      if (index && (index != t->index || capa != index->capa)) {
+        mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified");
+      }
+      return eql;
+    }
+  } 
+}
+
+/* Creates the hash table. */
+static htable*
+ht_new(mrb_state *mrb)
+{
+  htable *t;
+
+  t = (htable*)mrb_malloc(mrb, sizeof(htable));
+  t->size = 0;
+  t->rootseg =  NULL;
+  t->lastseg =  NULL;
+  t->last_len = 0;
+  t->index = NULL;
+
+  return t;
+}
+
+#define power2(v) do { \
+  v--;\
+  v |= v >> 1;\
+  v |= v >> 2;\
+  v |= v >> 4;\
+  v |= v >> 8;\
+  v |= v >> 16;\
+  v++;\
+} while (0)
+
+#ifndef UPPER_BOUND
+#define UPPER_BOUND(x) ((x)>>2|(x)>>1)
+#endif
+
+#define HT_MASK(index) ((index->capa)-1)
+
+/* Build index for the hash table */
+static void
+ht_index(mrb_state *mrb, htable *t)
+{
+  size_t size = (size_t)t->size;
+  size_t mask;
+  segindex *index = t->index;
+  segment *seg;
+  size_t i;
+
+  /* allocate index table */
+  if (index && index->size >= UPPER_BOUND(index->capa)) {
+    size = index->capa+1;
+  }
+  power2(size);
+  if (!index || index->capa < size) {
+    index = (segindex*)mrb_realloc_simple(mrb, index, sizeof(segindex)+sizeof(struct segkv*)*size);
+    if (index == NULL) {
+      mrb_free(mrb, index);
+      t->index = NULL;
+      return;
+    }
+    t->index = index;
+  }
+  index->size = t->size;
+  index->capa = size;
+  for (i=0; i<size; i++) {
+    index->table[i] = NULL;
+  }
+
+  /* rebuld index */
+  mask = HT_MASK(index);
+  seg = t->rootseg;
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      mrb_value key;
+      size_t k, step = 0;
+
+      if (!seg->next && i >= (size_t)t->last_len) {
+        return;
+      }
+      key = seg->e[i].key;
+      if (mrb_undef_p(key)) continue;
+      k = ht_hash_func(mrb, t, key) & mask;
+      while (index->table[k]) {
+        k = (k+(++step)) & mask;
+      }
+      index->table[k] = &seg->e[i];
+    }
+    seg = seg->next;
   }
 }
 
-KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal)
+/* Compacts the hash table removing deleted entries. */
+static void
+ht_compact(mrb_state *mrb, htable *t)
+{
+  segment *seg;
+  mrb_int i;
+  segment *seg2 = NULL;
+  mrb_int i2;
+  mrb_int size = 0;
+
+  if (t == NULL) return;
+  seg = t->rootseg;
+  if (t->index && (size_t)t->size == t->index->size) {
+    ht_index(mrb, t);
+    return;
+  }
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      mrb_value k = seg->e[i].key;
+
+      if (!seg->next && i >= t->last_len) {
+        goto exit;
+      }
+      if (mrb_undef_p(k)) {     /* found delete key */
+        if (seg2 == NULL) {
+          seg2 = seg;
+          i2 = i;
+        }
+      }
+      else {
+        size++;
+        if (seg2 != NULL) {
+          seg2->e[i2++] = seg->e[i];
+          if (i2 >= seg2->size) {
+            seg2 = seg2->next;
+            i2 = 0;
+          }
+        }
+      }
+    }
+    seg = seg->next;
+  }
+ exit:
+  /* reached at end */
+  t->size = size;
+  if (seg2) {
+    seg = seg2->next;
+    seg2->next = NULL;
+    t->last_len = i2;
+    t->lastseg = seg2;
+    while (seg) {
+      seg2 = seg->next;
+      mrb_free(mrb, seg);
+      seg = seg2;
+    }
+  }
+  if (t->index) {
+    ht_index(mrb, t);
+  }
+}
+
+static segment*
+segment_alloc(mrb_state *mrb, segment *seg)
+{
+  uint32_t size;
+
+  if (!seg) size = MRB_HT_INIT_SIZE;
+  else {
+    size = seg->size*HT_SEG_INCREASE_RATIO + 1;
+    if (size > UINT16_MAX) size = UINT16_MAX;
+  }
+
+  seg = (segment*)mrb_malloc(mrb, sizeof(segment)+sizeof(struct segkv)*size);
+  seg->size = size;
+  seg->next = NULL;
+
+  return seg;
+}
+
+/* Set the value for the key in the indexed table. */
+static void
+ht_index_put(mrb_state *mrb, htable *t, mrb_value key, mrb_value val)
+{
+  segindex *index = t->index;
+  size_t k, sp, step = 0, mask;
+  segment *seg;
+
+  if (index->size >= UPPER_BOUND(index->capa)) {
+    /* need to expand table */
+    ht_compact(mrb, t);
+    index = t->index;
+  }
+  mask = HT_MASK(index);
+  sp = index->capa;
+  k = ht_hash_func(mrb, t, key) & mask;
+  while (index->table[k]) {
+    mrb_value key2 = index->table[k]->key;
+    if (mrb_undef_p(key2)) {
+      if (sp == index->capa) sp = k;
+    }
+    else if (ht_hash_equal(mrb, t, key, key2)) {
+      index->table[k]->val = val;
+      return;
+    }
+    k = (k+(++step)) & mask;
+  }
+  if (sp < index->capa) {
+    k = sp;
+  }
+
+  /* put the value at the last */
+  seg = t->lastseg;
+  if (t->last_len < seg->size) {
+    index->table[k] = &seg->e[t->last_len++];
+  }
+  else {                        /* append a new segment */
+    seg->next = segment_alloc(mrb, seg);
+    seg = seg->next;
+    seg->next = NULL;
+    t->lastseg = seg;
+    t->last_len = 1;
+    index->table[k] = &seg->e[0];
+  }
+  index->table[k]->key = key;
+  index->table[k]->val = val;
+  index->size++;
+  t->size++;
+}
+
+/* Set the value for the key in the hash table. */
+static void
+ht_put(mrb_state *mrb, htable *t, mrb_value key, mrb_value val)
+{
+  segment *seg;
+  mrb_int i, deleted = 0;
+
+  if (t == NULL) return;
+  if (t->index) {
+    ht_index_put(mrb, t, key, val);
+    return;
+  }
+  seg = t->rootseg;
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      mrb_value k = seg->e[i].key;
+      /* Found room in last segment after last_len */
+      if (!seg->next && i >= t->last_len) {
+        seg->e[i].key = key;
+        seg->e[i].val = val;
+        t->last_len = i+1;
+        t->size++;
+        return;
+      }
+      if (mrb_undef_p(k)) {
+        deleted++;
+        continue;
+      }
+      if (ht_hash_equal(mrb, t, k, key)) {
+        seg->e[i].val = val;
+        return;
+      }
+    }
+    seg = seg->next;
+  }
+  /* Not found */
+
+  /* Compact if last segment has room */
+  if (deleted > 0 && deleted > MRB_HT_INIT_SIZE) {
+    ht_compact(mrb, t);
+  }
+  t->size++;
+
+  /* check if thre's room after compaction */
+  if (t->lastseg && t->last_len < t->lastseg->size) {
+    seg = t->lastseg;
+    i = t->last_len;
+  }
+  else {
+    /* append new segment */
+    seg = segment_alloc(mrb, t->lastseg);
+    i = 0;
+    if (t->rootseg == NULL) {
+      t->rootseg = seg;
+    }
+    else {
+      t->lastseg->next = seg;
+    }
+    t->lastseg = seg;
+  }
+  seg->e[i].key = key;
+  seg->e[i].val = val;
+  t->last_len = i+1;
+  if (t->index == NULL && t->size > MRB_HT_INIT_SIZE*4) {
+    ht_index(mrb, t);
+  }
+}
+
+/* Get a value for a key from the indexed table. */
+static mrb_bool
+ht_index_get(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp)
+{
+  segindex *index = t->index;
+  size_t mask = HT_MASK(index);
+  size_t k = ht_hash_func(mrb, t, key) & mask;
+  size_t step = 0;
+
+  while (index->table[k]) {
+    mrb_value key2 = index->table[k]->key;
+    if (!mrb_undef_p(key2) && ht_hash_equal(mrb, t, key, key2)) {
+      if (vp) *vp = index->table[k]->val;
+      return TRUE;
+    }
+    k = (k+(++step)) & mask;
+  }
+  return FALSE;
+}
+
+/* Get a value for a key from the hash table. */
+static mrb_bool
+ht_get(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp)
+{
+  segment *seg;
+  mrb_int i;
+
+  if (t == NULL) return FALSE;
+  if (t->index) {
+    return ht_index_get(mrb, t, key, vp);
+  }
+
+  seg = t->rootseg;
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      mrb_value k = seg->e[i].key;
+
+      if (!seg->next && i >= t->last_len) {
+        return FALSE;
+      }
+      if (mrb_undef_p(k)) continue;
+      if (ht_hash_equal(mrb, t, k, key)) {
+        if (vp) *vp = seg->e[i].val;
+        return TRUE;
+      }
+    }
+    seg = seg->next;
+  }
+  return FALSE;
+}
+
+/* Deletes the value for the symbol from the hash table. */
+/* Deletion is done by overwriting keys by `undef`. */
+static mrb_bool
+ht_del(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp)
+{
+  segment *seg;
+  mrb_int i;
+
+  if (t == NULL) return FALSE;
+  seg = t->rootseg;
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      mrb_value key2;
+
+      if (!seg->next && i >= t->last_len) {
+        /* not found */
+        return FALSE;
+      }
+      key2 = seg->e[i].key;
+      if (!mrb_undef_p(key2) && ht_hash_equal(mrb, t, key, key2)) {
+        if (vp) *vp = seg->e[i].val;
+        seg->e[i].key = mrb_undef_value();
+        t->size--;
+        return TRUE;
+      }
+    }
+    seg = seg->next;
+  }
+  return FALSE;
+}
+
+/* Iterates over the hash table. */
+static void
+ht_foreach(mrb_state *mrb, htable *t, mrb_hash_foreach_func *func, void *p)
+{
+  segment *seg;
+  mrb_int i;
+
+  if (t == NULL) return;
+  seg = t->rootseg;
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      /* no value in last segment after last_len */
+      if (!seg->next && i >= t->last_len) {
+        return;
+      }
+      if (mrb_undef_p(seg->e[i].key)) continue;
+      if ((*func)(mrb, seg->e[i].key, seg->e[i].val, p) != 0)
+        return;
+    }
+    seg = seg->next;
+  }
+}
+
+/* Iterates over the hash table. */
+MRB_API void
+mrb_hash_foreach(mrb_state *mrb, struct RHash *hash, mrb_hash_foreach_func *func, void *p)
+{
+  ht_foreach(mrb, hash->ht, func, p);
+}
+
+/* Copy the hash table. */
+static htable*
+ht_copy(mrb_state *mrb, htable *t)
+{
+  segment *seg;
+  htable *t2;
+  mrb_int i;
+
+  seg = t->rootseg;
+  t2 = ht_new(mrb);
+  if (t->size == 0) return t2;
+
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      mrb_value key = seg->e[i].key;
+      mrb_value val = seg->e[i].val;
+
+      if ((seg->next == NULL) && (i >= t->last_len)) {
+        return t2;
+      }
+      ht_put(mrb, t2, key, val);
+    }
+    seg = seg->next;
+  }
+  return t2;
+}
+
+/* Free memory of the hash table. */
+static void
+ht_free(mrb_state *mrb, htable *t)
+{
+  segment *seg;
+
+  if (!t) return;
+  seg = t->rootseg;
+  while (seg) {
+    segment *p = seg;
+    seg = seg->next;
+    mrb_free(mrb, p);
+  }
+  if (t->index) mrb_free(mrb, t->index);
+  mrb_free(mrb, t);
+}
 
 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)
+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);
@@ -103,75 +582,133 @@ mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
   return key;
 }
 
-#define KEY(key) mrb_hash_ht_key(mrb, key)
+#define KEY(key) ht_key(mrb, key)
+
+static int
+hash_mark_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
+{
+  mrb_gc_mark_value(mrb, key);
+  mrb_gc_mark_value(mrb, val);
+  return 0;
+}
 
 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);
-    }
-  }
+  ht_foreach(mrb, hash->ht, hash_mark_i, NULL);
 }
 
 size_t
 mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash)
 {
   if (!hash->ht) return 0;
-  return kh_size(hash->ht)*2;
+  return hash->ht->size*2;
 }
 
 void
 mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash)
 {
-  if (hash->ht) kh_destroy(ht, mrb, hash->ht);
+  ht_free(mrb, hash->ht);
 }
 
-
 MRB_API mrb_value
-mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
+mrb_hash_new(mrb_state *mrb)
 {
   struct RHash *h;
 
   h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
-  /* khash needs 1/4 empty space so it is not resized immediately */
-  if (capa == 0)
-    h->ht = 0;
-  else
-    h->ht = kh_init_size(ht, mrb, (khint_t)(capa*4/3));
+  h->ht = 0;
   h->iv = 0;
   return mrb_obj_value(h);
 }
 
 MRB_API mrb_value
-mrb_hash_new(mrb_state *mrb)
+mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
 {
-  return mrb_hash_new_capa(mrb, 0);
+  struct RHash *h;
+
+  h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
+  /* preallocate hash table */
+  h->ht = ht_new(mrb);
+  /* capacity ignored */
+  h->iv = 0;
+  return mrb_obj_value(h);
 }
 
 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);
 
+static mrb_value
+mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
+{
+  mrb_value orig;
+  struct RHash* copy;
+  htable *orig_h;
+  mrb_value ifnone, vret;
+
+  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");
+  }
+
+  orig_h = RHASH_TBL(self);
+  copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
+  copy->ht = ht_copy(mrb, orig_h);
+
+  if (MRB_RHASH_DEFAULT_P(self)) {
+    copy->flags |= MRB_HASH_DEFAULT;
+  }
+  if (MRB_RHASH_PROCDEFAULT_P(self)) {
+    copy->flags |= MRB_HASH_PROC_DEFAULT;
+  }
+  vret = mrb_obj_value(copy);
+  ifnone = RHASH_IFNONE(self);
+  if (!mrb_nil_p(ifnone)) {
+      mrb_iv_set(mrb, vret, mrb_intern_lit(mrb, "ifnone"), ifnone);
+  }
+  return vret;
+}
+
+static int
+check_kdict_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data)
+{
+  if (!mrb_symbol_p(key)) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys");
+  }
+  return 0;
+}
+
+void
+mrb_hash_check_kdict(mrb_state *mrb, mrb_value self)
+{
+  htable *t;
+
+  t = RHASH_TBL(self);
+  if (!t || t->size == 0) return;
+  ht_foreach(mrb, t, check_kdict_i, NULL);
+}
+
+MRB_API mrb_value
+mrb_hash_dup(mrb_state *mrb, mrb_value self)
+{
+  struct RHash* copy;
+  htable *orig_h;
+
+  orig_h = RHASH_TBL(self);
+  copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
+  copy->ht = orig_h ? ht_copy(mrb, orig_h) : NULL;
+  return mrb_obj_value(copy);
+}
+
 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_value val;
   mrb_sym mid;
 
-  if (h) {
-    k = kh_get(ht, mrb, h, key);
-    if (k != kh_end(h))
-      return kh_value(h, k).v;
+  if (ht_get(mrb, RHASH_TBL(hash), key, &val)) {
+    return val;
   }
 
   mid = mrb_intern_lit(mrb, "default");
@@ -185,15 +722,11 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value 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;
+  mrb_value val;
 
-  if (h) {
-    k = kh_get(ht, mrb, h, key);
-    if (k != kh_end(h))
-      return kh_value(h, k).v;
+  if (ht_get(mrb, RHASH_TBL(hash), key, &val)) {
+    return val;
   }
-
   /* not found */
   return def;
 }
@@ -201,94 +734,25 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value 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;
-  }
 
+  key = KEY(key);
+  ht_put(mrb, RHASH_TBL(hash), key, val);
   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_FROZEN_ERROR, "can't modify frozen hash");
   }
-  mrb_hash_tbl(mrb, hash);
+
+  if (!RHASH_TBL(hash)) {
+    RHASH_TBL(hash) = ht_new(mrb);
+  }
 }
 
 /* 15.2.13.4.16 */
@@ -528,47 +992,17 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
 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;
-    }
+  htable *t = RHASH_TBL(hash);
+  mrb_value del_val;
+
+  if (ht_del(mrb, t, key, &del_val)) {
+    return del_val;
   }
 
   /* 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)
 {
@@ -579,6 +1013,33 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
   return mrb_hash_delete_key(mrb, self, key);
 }
 
+/* find first element in the hash table, and remove it. */
+static void
+ht_shift(mrb_state *mrb, htable *t, mrb_value *kp, mrb_value *vp)
+{
+  segment *seg = t->rootseg;
+  mrb_int i;
+
+  while (seg) {
+    for (i=0; i<seg->size; i++) {
+      mrb_value key;
+
+      if (!seg->next && i >= t->last_len) {
+        return;
+      }
+      key = seg->e[i].key;
+      if (mrb_undef_p(key)) continue;
+      *kp = key;
+      *vp = seg->e[i].val;
+      /* delete element */
+      seg->e[i].key = mrb_undef_value();
+      t->size--;
+      return;
+    }
+    seg = seg->next;
+  }
+}
+
 /* 15.2.13.4.24 */
 /*
  *  call-seq:
@@ -596,22 +1057,16 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
 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;
+  htable *t = RHASH_TBL(hash);
 
   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;
+  if (t && t->size > 0) {
+    mrb_value del_key, del_val;
 
-      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);
-    }
+    ht_shift(mrb, t, &del_key, &del_val);
+    mrb_gc_protect(mrb, del_key);
+    mrb_gc_protect(mrb, del_val);
+    return mrb_assoc_new(mrb, del_key, del_val);
   }
 
   if (MRB_RHASH_DEFAULT_P(hash)) {
@@ -640,10 +1095,13 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash)
 MRB_API mrb_value
 mrb_hash_clear(mrb_state *mrb, mrb_value hash)
 {
-  khash_t(ht) *h = RHASH_TBL(hash);
+  htable *t = RHASH_TBL(hash);
 
   mrb_hash_modify(mrb, hash);
-  if (h) kh_clear(ht, mrb, h);
+  if (t) {
+    ht_free(mrb, t);
+    RHASH_TBL(hash) = NULL;
+  }
   return hash;
 }
 
@@ -676,6 +1134,15 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
   return val;
 }
 
+MRB_API mrb_int
+mrb_hash_size(mrb_state *mrb, mrb_value hash)
+{
+  htable *t = RHASH_TBL(hash);
+
+  if (!t) return 0;
+  return t->size;
+}
+
 /* 15.2.13.4.20 */
 /* 15.2.13.4.25 */
 /*
@@ -693,10 +1160,17 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
 static mrb_value
 mrb_hash_size_m(mrb_state *mrb, mrb_value self)
 {
-  khash_t(ht) *h = RHASH_TBL(self);
+  mrb_int size = mrb_hash_size(mrb, self);
+  return mrb_fixnum_value(size);
+}
 
-  if (!h) return mrb_fixnum_value(0);
-  return mrb_fixnum_value(kh_size(h));
+MRB_API mrb_bool
+mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
+{
+  htable *t = RHASH_TBL(self);
+
+  if (!t) return TRUE;
+  return t->size == 0;
 }
 
 /* 15.2.13.4.12 */
@@ -709,27 +1183,17 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self)
  *     {}.empty?   #=> true
  *
  */
-MRB_API mrb_value
-mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
+static mrb_value
+mrb_hash_empty_m(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();
+  return mrb_bool_value(mrb_hash_empty_p(mrb, self));
 }
 
-/* 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)
+static int
+hash_keys_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
 {
-  return hash;
+  mrb_ary_push(mrb, *(mrb_value*)p, key);
+  return 0;
 }
 
 /* 15.2.13.4.19 */
@@ -748,33 +1212,24 @@ mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
 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;
+  htable *t = RHASH_TBL(hash);
+  mrb_int size;
   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;
-      }
-    }
-  }
+
+  if (!t || (size = t->size) == 0)
+    return mrb_ary_new(mrb);
+  ary = mrb_ary_new_capa(mrb, size);
+  ht_foreach(mrb, t, hash_keys_i, (void*)&ary);
   return ary;
 }
 
+static int
+hash_vals_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
+{
+  mrb_ary_push(mrb, *(mrb_value*)p, val);
+  return 0;
+}
+
 /* 15.2.13.4.28 */
 /*
  *  call-seq:
@@ -791,19 +1246,14 @@ mrb_hash_keys(mrb_state *mrb, mrb_value hash)
 MRB_API mrb_value
 mrb_hash_values(mrb_state *mrb, mrb_value hash)
 {
-  khash_t(ht) *h = RHASH_TBL(hash);
-  khiter_t k;
+  htable *t = RHASH_TBL(hash);
+  mrb_int size;
   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);
-    }
-  }
+  if (!t || (size = t->size) == 0)
+    return mrb_ary_new(mrb);
+  ary = mrb_ary_new_capa(mrb, size);
+  ht_foreach(mrb, t, hash_vals_i, (void*)&ary);
   return ary;
 }
 
@@ -826,21 +1276,44 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash)
  *
  */
 
+MRB_API mrb_bool
+mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key)
+{
+  htable *t;
+
+  t = RHASH_TBL(hash);
+  if (ht_get(mrb, t, key, NULL)) {
+    return TRUE;
+  }
+  return 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_bool key_p;
 
   mrb_get_args(mrb, "o", &key);
+  key_p = mrb_hash_key_p(mrb, hash, key);
+  return mrb_bool_value(key_p);
+}
 
-  h = RHASH_TBL(hash);
-  if (h) {
-    k = kh_get(ht, mrb, h, key);
-    return mrb_bool_value(k != kh_end(h));
+struct has_v_arg {
+  mrb_bool found;
+  mrb_value val;
+};
+
+static int
+hash_has_value_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
+{
+  struct has_v_arg *arg = (struct has_v_arg*)p;
+  
+  if (mrb_equal(mrb, arg->val, val)) {
+    arg->found = TRUE;
+    return 1;
   }
-  return mrb_false_value();
+  return 0;
 }
 
 /* 15.2.13.4.14 */
@@ -862,22 +1335,62 @@ static mrb_value
 mrb_hash_has_value(mrb_state *mrb, mrb_value hash)
 {
   mrb_value val;
-  khash_t(ht) *h;
-  khiter_t k;
-
+  struct has_v_arg arg;
+  
   mrb_get_args(mrb, "o", &val);
-  h = RHASH_TBL(hash);
+  arg.found = FALSE;
+  arg.val = val;
+  ht_foreach(mrb, RHASH_TBL(hash), hash_has_value_i, &arg);
+  return mrb_bool_value(arg.found);
+}
 
-  if (h) {
-    for (k = kh_begin(h); k != kh_end(h); k++) {
-      if (!kh_exist(h, k)) continue;
+static int
+merge_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data)
+{
+  htable *h1 = (htable*)data;
 
-      if (mrb_equal(mrb, kh_value(h, k).v, val)) {
-        return mrb_true_value();
-      }
-    }
+  ht_put(mrb, h1, key, val);
+  return 0;
+}
+
+MRB_API void
+mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2)
+{
+  htable *h1, *h2;
+
+  mrb_hash_modify(mrb, hash1);
+  hash2 = mrb_ensure_hash_type(mrb, hash2);
+  h1 = RHASH_TBL(hash1);
+  h2 = RHASH_TBL(hash2);
+
+  if (!h2) return;
+  if (!h1) {
+    RHASH_TBL(hash1) = ht_copy(mrb, h2);
+    return;
   }
-  return mrb_false_value();
+  ht_foreach(mrb, h2, merge_i, h1);
+  mrb_write_barrier(mrb, (struct RBasic*)RHASH(hash1));
+  return;
+}
+
+/*
+ *  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.rehash   #=> {"AA"=>"b"}
+ *     h["AA"]    #=> "b"
+ */
+static mrb_value
+mrb_hash_rehash(mrb_state *mrb, mrb_value self)
+{
+  ht_compact(mrb, RHASH_TBL(self));
+  return self;
 }
 
 void
@@ -888,6 +1401,7 @@ mrb_init_hash(mrb_state *mrb)
   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, "initialize_copy", mrb_hash_init_copy,   MRB_ARGS_REQ(1));
   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  */
@@ -896,7 +1410,7 @@ mrb_init_hash(mrb_state *mrb)
   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, "empty?",          mrb_hash_empty_m,     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 */
@@ -906,11 +1420,9 @@ mrb_init_hash(mrb_state *mrb)
   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)*/
+  mrb_define_method(mrb, h, "rehash",          mrb_hash_rehash,      MRB_ARGS_NONE());
 }
index e9dc93b..7890e3d 100644 (file)
 #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)
 {
-  mrb_method_t m = mrb_method_search(mrb, mrb_class(mrb, obj), mid);
+  struct RClass *c = mrb_class(mrb, obj);
+  mrb_method_t m = mrb_method_search_vm(mrb, &c, mid);
   struct RProc *p;
 
   if (MRB_METHOD_UNDEF_P(m)) return FALSE;
@@ -252,18 +239,18 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
   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) {
+  if (sc->flags & MRB_FL_CLASS_IS_PREPENDED) {
     struct RClass *c0 = sc->super;
     struct RClass *c1 = dc;
 
     /* copy prepended iclasses */
-    while (!(c0->flags & MRB_FLAG_IS_ORIGIN)) {
+    while (!(c0->flags & MRB_FL_CLASS_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;
+    c1->super->flags |= MRB_FL_CLASS_IS_ORIGIN;
   }
   if (sc->mt) {
     dc->mt = kh_copy(mt, mrb, sc->mt);
@@ -279,10 +266,15 @@ static void
 init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
 {
   switch (mrb_type(obj)) {
+    case MRB_TT_ICLASS:
+      copy_class(mrb, dest, obj);
+      return;
     case MRB_TT_CLASS:
     case MRB_TT_MODULE:
       copy_class(mrb, dest, obj);
-      /* fall through */
+      mrb_iv_copy(mrb, dest, obj);
+      mrb_iv_remove(mrb, dest, mrb_intern_lit(mrb, "__classname__"));
+      break;
     case MRB_TT_OBJECT:
     case MRB_TT_SCLASS:
     case MRB_TT_HASH:
@@ -343,6 +335,7 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self)
   mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c);
   clone = mrb_obj_value(p);
   init_copy(mrb, clone, self);
+  p->flags |= mrb_obj_ptr(self)->flags & MRB_FL_OBJ_IS_FROZEN;
 
   return clone;
 }
@@ -499,7 +492,7 @@ mrb_obj_frozen(mrb_state *mrb, mrb_value self)
  *  <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
+static mrb_value
 mrb_obj_hash(mrb_state *mrb, mrb_value self)
 {
   return mrb_fixnum_value(mrb_obj_id(self));
@@ -545,96 +538,6 @@ obj_is_instance_of(mrb_state *mrb, mrb_value self)
   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 */
 /*
@@ -675,124 +578,6 @@ mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self)
 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 || kh_size(h) == 0) return;
-  for (i=0;i<kh_end(h);i++) {
-    if (kh_exist(h, i)) {
-      mrb_method_t m = kh_value(h, i);
-      if (MRB_METHOD_UNDEF_P(m)) continue;
-      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_capa(mrb, kh_size(set));
-  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:
@@ -807,57 +592,6 @@ 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 */
 /*
@@ -906,16 +640,6 @@ mrb_f_raise(mrb_state *mrb, mrb_value self)
   return mrb_nil_value();            /* not reached */
 }
 
-static mrb_value
-mrb_krn_class_defined(mrb_state *mrb, mrb_value self)
-{
-  mrb_value str;
-
-  mrb_get_args(mrb, "S", &str);
-  return mrb_bool_value(mrb_class_defined(mrb, RSTRING_PTR(str)));
-}
-
-
 /* 15.3.1.3.41 */
 /*
  *  call-seq:
@@ -945,7 +669,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
   mrb_value val;
 
   mrb_get_args(mrb, "n", &sym);
-  mrb_iv_check(mrb, sym);
+  mrb_iv_name_sym_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));
@@ -1012,6 +736,7 @@ 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:
@@ -1031,45 +756,16 @@ basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
 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);
-  }
+  mrb_bool priv = FALSE, respond_to_p;
 
+  mrb_get_args(mrb, "n|b", &id, &priv);
+  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[0] = mrb_symbol_value(id);
       args[1] = mrb_bool_value(priv);
       v = mrb_funcall_argv(mrb, self, rtm_id, 2, args);
       return mrb_bool_value(mrb_bool(v));
@@ -1078,67 +774,6 @@ obj_respond_to(mrb_state *mrb, mrb_value self)
   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_method_t m;
-  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_METHOD_FROM_PROC(m, p);
-  mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, m);
-  return mrb_symbol_value(mid);
-}
-
 static mrb_value
 mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
 {
@@ -1156,51 +791,8 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
   return mrb_false_value();
 }
 
-/* 15.3.1.2.7 */
-/*
- *  call-seq:
- *     local_variables   -> array
- *
- *  Returns the names of local variables in the current scope.
- *
- *  [mruby limitation]
- *  If variable symbol information was stripped out from
- *  compiled binary files using `mruby-strip -l`, this
- *  method always returns an empty array.
- */
-static mrb_value
-mrb_local_variables(mrb_state *mrb, mrb_value self)
-{
-  struct RProc *proc;
-  mrb_irep *irep;
-  mrb_value vars;
-  size_t i;
-
-  proc = mrb->c->ci[-1].proc;
-
-  if (MRB_PROC_CFUNC_P(proc)) {
-    return mrb_ary_new(mrb);
-  }
-  vars = mrb_hash_new(mrb);
-  while (proc) {
-    if (MRB_PROC_CFUNC_P(proc)) break;
-    irep = proc->body.irep;
-    if (!irep->lv) break;
-    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 (!MRB_PROC_ENV_P(proc)) break;
-    proc = proc->upper;
-    //if (MRB_PROC_SCOPE_P(proc)) break;
-    if (!proc->c) break;
-  }
-
-  return mrb_hash_keys(mrb, vars);
-}
-
 mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
+
 void
 mrb_init_kernel(mrb_state *mrb)
 {
@@ -1208,13 +800,10 @@ mrb_init_kernel(mrb_state *mrb)
 
   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  */
@@ -1231,34 +820,23 @@ mrb_init_kernel(mrb_state *mrb)
   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_define_method(mrb, krn, "class_defined?",             mrb_krn_class_defined,           MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, krn, "__to_int",                   mrb_to_int,                      MRB_ARGS_NONE()); /* internal */
+  mrb_define_method(mrb, krn, "__to_str",                   mrb_to_str,                      MRB_ARGS_NONE()); /* 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"));
+  mrb_define_alias(mrb, mrb->module_class, "dup", "clone"); /* XXX */
 }
index 3a6ce7c..ab03467 100644 (file)
@@ -7,6 +7,7 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 #include <mruby/dump.h>
 #include <mruby/irep.h>
 #include <mruby/proc.h>
@@ -40,6 +41,23 @@ offset_crc_body(void)
   return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc);
 }
 
+#ifndef MRB_WITHOUT_FLOAT
+static double
+str_to_double(mrb_state *mrb, mrb_value str)
+{
+  const char *p = RSTRING_PTR(str);
+  mrb_int len = RSTRING_LEN(str);
+
+  /* `i`, `inf`, `infinity` */
+  if (len > 0 && p[0] == 'i') return INFINITY;
+
+  /* `I`, `-inf`, `-infinity` */
+  if (p[0] == 'I' || (len > 1 && p[0] == '-' && p[1] == 'i')) return -INFINITY;
+
+  return mrb_str_to_dbl(mrb, str, TRUE);
+}
+#endif
+
 static mrb_irep*
 read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
 {
@@ -68,7 +86,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
 
   /* Binary Data Section */
   /* ISEQ BLOCK */
-  irep->ilen = (size_t)bin_to_uint32(src);
+  irep->ilen = (uint16_t)bin_to_uint32(src);
   src += sizeof(uint32_t);
   src += skip_padding(src);
 
@@ -79,27 +97,14 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
     if ((flags & FLAG_SRC_MALLOC) == 0 &&
         (flags & FLAG_BYTEORDER_NATIVE)) {
       irep->iseq = (mrb_code*)src;
-      src += sizeof(uint32_t) * irep->ilen;
+      src += sizeof(mrb_code) * 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);
-        }
-      }
+      size_t data_len = sizeof(mrb_code) * irep->ilen;
+      irep->iseq = (mrb_code *)mrb_malloc(mrb, data_len);
+      memcpy(irep->iseq, src, data_len);
+      src += data_len;
     }
   }
 
@@ -128,12 +133,17 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
       switch (tt) { /* pool data */
       case IREP_TT_FIXNUM: {
         mrb_value num = mrb_str_to_inum(mrb, s, 10, FALSE);
+#ifdef MRB_WITHOUT_FLOAT
+        irep->pool[i] = num;
+#else
         irep->pool[i] = mrb_float_p(num)? mrb_float_pool(mrb, mrb_float(num)) : num;
-      } break;
+#endif
+        }
+        break;
 
 #ifndef MRB_WITHOUT_FLOAT
       case IREP_TT_FLOAT:
-        irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE));
+        irep->pool[i] = mrb_float_pool(mrb, str_to_double(mrb, s));
         break;
 #endif
 
@@ -152,7 +162,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
   }
 
   /* SYMS BLOCK */
-  irep->slen = (size_t)bin_to_uint32(src);  /* syms length */
+  irep->slen = (uint16_t)bin_to_uint32(src);  /* syms length */
   src += sizeof(uint32_t);
   if (irep->slen > 0) {
     if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) {
@@ -223,12 +233,11 @@ read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
   return read_irep_record(mrb, bin, &len, flags);
 }
 
+/* ignore lineno record */
 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 */
@@ -236,9 +245,6 @@ read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_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;
 
@@ -249,15 +255,11 @@ read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_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;
 }
 
@@ -315,7 +317,6 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_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;
@@ -328,8 +329,6 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
     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);
index bb3d7b6..73fddb2 100644 (file)
@@ -5,16 +5,15 @@ MRuby.each_target do
 
   objs = Dir.glob("#{current_dir}/*.c").map { |f|
     next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/
-    next nil if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT") and f =~ /fmt_fp.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
+  self.libmruby_objs << objs
 
-  file libfile("#{build_dir}/lib/libmruby_core") => objs do |t|
+  file libmruby_core_static => objs do |t|
     archiver.run t.name, t.prerequisites
   end
 end
index b898fd0..4128ea3 100644 (file)
@@ -10,6 +10,7 @@
 #endif
 #include <limits.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include <mruby.h>
 #include <mruby/array.h>
@@ -23,9 +24,9 @@
 #define floor(f) floorf(f)
 #define ceil(f) ceilf(f)
 #define fmod(x,y) fmodf(x,y)
-#define MRB_FLO_TO_STR_FMT "%.7g"
+#define FLO_TO_STR_PREC 8
 #else
-#define MRB_FLO_TO_STR_FMT "%.14g"
+#define FLO_TO_STR_PREC 16
 #endif
 #endif
 
@@ -153,6 +154,22 @@ num_div(mrb_state *mrb, mrb_value x)
 #endif
 }
 
+static mrb_value
+num_coerce_step_counter(mrb_state *mrb, mrb_value self)
+{
+  mrb_value counter = self, num, step;
+
+  mrb_get_args(mrb, "oo", &num, &step);
+
+#ifndef MRB_WITHOUT_FLOAT
+  if (mrb_float_p(self) || mrb_float_p(num) || mrb_float_p(step)) {
+    counter = mrb_funcall(mrb, counter, "to_f", 0);
+  }
+#endif
+
+  return counter;
+}
+
 #ifndef MRB_WITHOUT_FLOAT
 /********************************************************************
  *
@@ -177,10 +194,49 @@ num_div(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_to_s(mrb_state *mrb, mrb_value flt)
 {
-  if (isnan(mrb_float(flt))) {
+  mrb_float f = mrb_float(flt);
+
+  if (isinf(f)) {
+    return f < 0 ? mrb_str_new_lit(mrb, "-Infinity")
+                 : mrb_str_new_lit(mrb, "Infinity");
+  }
+  else if (isnan(f)) {
     return mrb_str_new_lit(mrb, "NaN");
   }
-  return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT);
+  else {
+    char fmt[] = "%." MRB_STRINGIZE(FLO_TO_STR_PREC) "g";
+    mrb_value str = mrb_float_to_str(mrb, flt, fmt);
+    mrb_int len;
+    char *begp;
+
+    insert_dot_zero:
+    begp = RSTRING_PTR(str);
+    len = RSTRING_LEN(str);
+    for (char *p = begp, *endp = p + len; p < endp; ++p) {
+      if (*p == '.') {
+        return str;
+      }
+      else if (*p == 'e') {
+        ptrdiff_t e_pos = p - begp;
+        mrb_str_cat(mrb, str, ".0", 2);
+        p = RSTRING_PTR(str) + e_pos;
+        memmove(p + 2, p, len - e_pos);
+        memcpy(p, ".0", 2);
+        return str;
+      }
+    }
+
+    if (FLO_TO_STR_PREC + (begp[0] == '-') <= len) {
+      --fmt[sizeof(fmt) - 3];  /* %.16g(%.8g) -> %.15g(%.7g) */
+      str = mrb_float_to_str(mrb, flt, fmt);
+      goto insert_dot_zero;
+    }
+    else {
+      mrb_str_cat(mrb, str, ".0", 2);
+    }
+
+    return str;
+  }
 }
 
 /* 15.2.9.3.2  */
@@ -220,29 +276,40 @@ flo_mul(mrb_state *mrb, mrb_value x)
 }
 
 static void
-flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *modp)
+flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
 {
-  mrb_float div;
-  mrb_float mod;
+  double div, mod;
 
+  if (isnan(y)) {
+    /* y is NaN so all results are NaN */
+    div = mod = y;
+    goto exit;
+  }
   if (y == 0.0) {
-    if (x > 0.0) div = INFINITY;
-    else if (x < 0.0) div = -INFINITY;
-    else div = NAN;             /* x == 0.0 */
+    if (x == 0) div = NAN;
+    else if (x > 0.0) div = INFINITY;
+    else div = -INFINITY;       /* x < 0.0 */
     mod = NAN;
+    goto exit;
+  }
+  if ((x == 0.0) || (isinf(y) && !isinf(x))) {
+    mod = x;
   }
   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 (isinf(x) && !isinf(y)) {
+    div = x;
+  }
+  else {
+    div = (x - mod) / y;
+    if (modp && divp) div = round(div);
+  }
+  if (y*mod < 0) {
+    mod += y;
+    div -= 1.0;
+  }
+ exit:
   if (modp) *modp = mod;
   if (divp) *divp = div;
 }
@@ -302,7 +369,7 @@ flo_eql(mrb_state *mrb, mrb_value x)
 
   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));
+  return mrb_bool_value(mrb_float(x) == mrb_float(y));
 }
 
 /* 15.2.9.3.7  */
@@ -422,7 +489,7 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
     val = trunc(val);
 #else
     if (val > 0){
-        val = floor(val);    
+        val = floor(val);
     } else {
         val = ceil(val);
     }
@@ -663,7 +730,6 @@ flo_round(mrb_state *mrb, mrb_value num)
 /*
  *  call-seq:
  *     flt.to_i      ->  integer
- *     flt.to_int    ->  integer
  *     flt.truncate  ->  integer
  *
  *  Returns <i>flt</i> truncated to an <code>Integer</code>.
@@ -703,7 +769,6 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
 /*
  *  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.
@@ -1141,7 +1206,7 @@ fix_to_f(mrb_state *mrb, mrb_value num)
  *  (in particular infinite or NaN)
  *  to numerical classes which don't support them.
  *
- *     Float::INFINITY.to_r
+ *     Float::INFINITY.to_i
  *
  *  <em>raises the exception:</em>
  *
@@ -1160,12 +1225,7 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
   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");
-    }
+    mrb_check_num_exact(mrb, d);
     if (FIXABLE_FLOAT(d)) {
       z = (mrb_int)d;
     }
@@ -1478,11 +1538,13 @@ flo_plus(mrb_state *mrb, mrb_value x)
 void
 mrb_init_numeric(mrb_state *mrb)
 {
-  struct RClass *numeric, *integer, *fixnum;
+  struct RClass *numeric, *integer, *fixnum, *integral;
 #ifndef MRB_WITHOUT_FLOAT
   struct RClass *fl;
 #endif
 
+  integral = mrb_define_module(mrb, "Integral");
+
   /* Numeric Class */
   numeric = mrb_define_class(mrb, "Numeric",  mrb->object_class);                /* 15.2.7 */
 
@@ -1496,6 +1558,7 @@ mrb_init_numeric(mrb_state *mrb)
   mrb_define_method(mrb, numeric, ">=",       num_ge,          MRB_ARGS_REQ(1));
   mrb_define_method(mrb, numeric, "finite?",  num_finite_p,    MRB_ARGS_NONE());
   mrb_define_method(mrb, numeric, "infinite?",num_infinite_p,  MRB_ARGS_NONE());
+  mrb_define_method(mrb, numeric, "__coerce_step_counter", num_coerce_step_counter,  MRB_ARGS_REQ(2));
 
   /* Integer Class */
   integer = mrb_define_class(mrb, "Integer",  numeric);                          /* 15.2.8 */
@@ -1569,6 +1632,7 @@ mrb_init_numeric(mrb_state *mrb)
 #ifdef NAN
   mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
 #endif
+
+  mrb_include_module(mrb, fl, integral);
 #endif
-  mrb_define_module(mrb, "Integral");
 }
index 8724c54..d45ab27 100644 (file)
@@ -323,19 +323,6 @@ convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *metho
 }
 
 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;
@@ -447,8 +434,10 @@ mrb_any_to_s(mrb_state *mrb, mrb_value 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)));
+  if (!mrb_immediate_p(obj)) {
+    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;
@@ -505,25 +494,22 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
   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");
+
+  if (!mrb_fixnum_p(val)) {
+    mrb_value type;
+
+#ifndef MRB_WITHOUT_FLOAT
+    if (mrb_float_p(val)) {
+      return mrb_flo_to_fixnum(mrb, val);
+    }
+#endif
+    type = inspect_type(mrb, val);
+    mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer", type);
+  }
+  return val;
 }
 
 MRB_API mrb_value
@@ -533,18 +519,12 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
 
   if (mrb_nil_p(val)) {
     if (base != 0) goto arg_error;
-      mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
+    mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
   }
   switch (mrb_type(val)) {
 #ifndef MRB_WITHOUT_FLOAT
     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);
 #endif
 
@@ -568,11 +548,8 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
 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) || !mrb_fixnum_p(tmp)) {
-    tmp = mrb_to_integer(mrb, val, "to_i");
-  }
-  return tmp;
+  /* to raise TypeError */
+  return mrb_to_int(mrb, val);
 }
 
 MRB_API mrb_value
@@ -605,6 +582,74 @@ mrb_Float(mrb_state *mrb, mrb_value val)
 #endif
 
 MRB_API mrb_value
+mrb_to_str(mrb_state *mrb, mrb_value val)
+{
+  if (!mrb_string_p(val)) {
+    mrb_value type = inspect_type(mrb, val);
+    mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to String", type);
+  }
+  return val;
+}
+
+/* obsolete: use mrb_ensure_string_type() instead */
+MRB_API mrb_value
+mrb_string_type(mrb_state *mrb, mrb_value str)
+{
+  return mrb_ensure_string_type(mrb, str);
+}
+
+MRB_API mrb_value
+mrb_ensure_string_type(mrb_state *mrb, mrb_value str)
+{
+  if (!mrb_string_p(str)) {
+    mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to String",
+               inspect_type(mrb, str));
+  }
+  return str;
+}
+
+MRB_API mrb_value
+mrb_check_string_type(mrb_state *mrb, mrb_value str)
+{
+  if (!mrb_string_p(str)) return mrb_nil_value();
+  return str;
+}
+
+MRB_API mrb_value
+mrb_ensure_array_type(mrb_state *mrb, mrb_value ary)
+{
+  if (!mrb_array_p(ary)) {
+    mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Array",
+               inspect_type(mrb, ary));
+  }
+  return ary;
+}
+
+MRB_API mrb_value
+mrb_check_array_type(mrb_state *mrb, mrb_value ary)
+{
+  if (!mrb_array_p(ary)) return mrb_nil_value();
+  return ary;
+}
+
+MRB_API mrb_value
+mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash)
+{
+  if (!mrb_hash_p(hash)) {
+    mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Hash",
+               inspect_type(mrb, hash));
+  }
+  return hash;
+}
+
+MRB_API mrb_value
+mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
+{
+  if (!mrb_hash_p(hash)) return mrb_nil_value();
+  return hash;
+}
+
+MRB_API mrb_value
 mrb_inspect(mrb_state *mrb, mrb_value obj)
 {
   return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0));
index c6e9be4..dab95e4 100644 (file)
@@ -10,7 +10,7 @@
 #include <mruby/opcode.h>
 
 static mrb_code call_iseq[] = {
-  MKOP_A(OP_CALL, 0),
+  OP_CALL,
 };
 
 struct RProc*
@@ -63,12 +63,12 @@ closure_setup(mrb_state *mrb, struct RProc *p)
 {
   mrb_callinfo *ci = mrb->c->ci;
   struct RProc *up = p->upper;
-  struct REnv *e;
+  struct REnv *e = NULL;
 
-  if (ci->env) {
+  if (ci && ci->env) {
     e = ci->env;
   }
-  else {
+  else if (up) {
     struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
 
     e = env_new(mrb, up->body.irep->nlocals);
@@ -78,9 +78,11 @@ closure_setup(mrb_state *mrb, struct RProc *p)
       mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
     }
   }
-  p->e.env = e;
-  p->flags |= MRB_PROC_ENVSET;
-  mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+  if (e) {
+    p->e.env = e;
+    p->flags |= MRB_PROC_ENVSET;
+    mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+  }
 }
 
 struct RProc*
@@ -223,7 +225,7 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self)
 {
   struct RProc *p = mrb_proc_ptr(self);
   struct mrb_irep *irep;
-  mrb_code *iseq;
+  mrb_code *pc;
   mrb_aspec aspec;
   int ma, op, ra, pa, arity;
 
@@ -237,13 +239,13 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self)
     return mrb_fixnum_value(0);
   }
 
-  iseq = irep->iseq;
+  pc = irep->iseq;
   /* arity is depend on OP_ENTER */
-  if (GET_OPCODE(*iseq) != OP_ENTER) {
+  if (*pc != OP_ENTER) {
     return mrb_fixnum_value(0);
   }
 
-  aspec = GETARG_Ax(*iseq);
+  aspec = PEEK_W(pc+1);
   ma = MRB_ASPEC_REQ(aspec);
   op = MRB_ASPEC_OPT(aspec);
   ra = MRB_ASPEC_REST(aspec);
@@ -299,7 +301,7 @@ mrb_init_proc(mrb_state *mrb)
   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_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
   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());
 
@@ -308,6 +310,6 @@ mrb_init_proc(mrb_state *mrb)
   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 */
+  mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.2.6  */
+  mrb_define_method(mrb, mrb->kernel_module,       "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */
 }
index 0360b48..21771c8 100644 (file)
 #include <mruby/string.h>
 #include <mruby/array.h>
 
-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;
-}
+#define RANGE_INITIALIZED_MASK 1
+#define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_MASK)
+#define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_MASK)
 
 static void
-range_check(mrb_state *mrb, mrb_value a, mrb_value b)
+r_check(mrb_state *mrb, mrb_value a, mrb_value b)
 {
   mrb_value ans;
   enum mrb_vtype ta;
@@ -46,18 +39,83 @@ range_check(mrb_state *mrb, mrb_value a, mrb_value b)
   }
 }
 
-MRB_API mrb_value
-mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
+static mrb_bool
+r_le(mrb_state *mrb, mrb_value a, mrb_value b)
 {
-  struct RRange *r;
+  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 */
 
-  range_check(mrb, beg, end);
-  r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
+  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;
+}
+
+static void
+range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
+{
+#ifndef MRB_RANGE_EMBED
   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);
+#endif
+}
+
+static struct RRange *
+range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+  r_check(mrb, beg, end);
+
+  if (r) {
+    if (RANGE_INITIALIZED_P(r)) {
+      /* Ranges are immutable, so that they should be initialized only once. */
+      mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
+    }
+    else {
+      range_ptr_alloc_edges(mrb, r);
+    }
+  }
+  else {
+    r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
+    range_ptr_alloc_edges(mrb, r);
+  }
+
+  RANGE_BEG(r) = beg;
+  RANGE_END(r) = end;
+  RANGE_EXCL(r) = excl;
+  RANGE_INITIALIZED(r);
+
+  return r;
+}
+
+static void
+range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+  range_ptr_init(mrb, r, beg, end, excl);
+  mrb_write_barrier(mrb, (struct RBasic*)r);
 }
 
 /*
@@ -67,12 +125,10 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
  *
  *  Returns the first object in <i>rng</i>.
  */
-mrb_value
-mrb_range_beg(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_beg(mrb_state *mrb, mrb_value range)
 {
-  struct RRange *r = mrb_range_ptr(mrb, range);
-
-  return r->edges->beg;
+  return mrb_range_beg(mrb, range);
 }
 
 /*
@@ -85,13 +141,10 @@ mrb_range_beg(mrb_state *mrb, mrb_value range)
  *     (1..10).end    #=> 10
  *     (1...10).end   #=> 10
  */
-
-mrb_value
-mrb_range_end(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_end(mrb_state *mrb, mrb_value range)
 {
-  struct RRange *r = mrb_range_ptr(mrb, range);
-
-  return r->edges->end;
+  return mrb_range_end(mrb, range);
 }
 
 /*
@@ -100,27 +153,12 @@ mrb_range_end(mrb_state *mrb, mrb_value range)
  *
  *  Returns <code>true</code> if <i>range</i> excludes its end value.
  */
-mrb_value
-mrb_range_excl(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_excl(mrb_state *mrb, mrb_value range)
 {
-  struct RRange *r = mrb_range_ptr(mrb, range);
-
-  return mrb_bool_value(r->excl);
+  return mrb_bool_value(mrb_range_excl_p(mrb, range));
 }
 
-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
@@ -129,25 +167,17 @@ range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bo
  *  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)
+static mrb_value
+range_initialize(mrb_state *mrb, mrb_value range)
 {
   mrb_value beg, end;
-  mrb_bool exclusive;
-  mrb_int n;
+  mrb_bool exclusive = FALSE;
 
-  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);
+  mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
+  range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
   return range;
 }
+
 /*
  *  call-seq:
  *     range == obj    => true or false
@@ -160,11 +190,9 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
  *    (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)
+static mrb_value
+range_eq(mrb_state *mrb, mrb_value range)
 {
   struct RRange *rr;
   struct RRange *ro;
@@ -179,60 +207,22 @@ mrb_range_eq(mrb_state *mrb, mrb_value range)
 
   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) {
+  v1 = mrb_funcall(mrb, RANGE_BEG(rr), "==", 1, RANGE_BEG(ro));
+  v2 = mrb_funcall(mrb, RANGE_END(rr), "==", 1, RANGE_END(ro));
+  if (!mrb_bool(v1) || !mrb_bool(v2) || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
     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)
+static mrb_value
+range_include(mrb_state *mrb, mrb_value range)
 {
   mrb_value val;
   struct RRange *r = mrb_range_ptr(mrb, range);
@@ -241,48 +231,15 @@ mrb_range_include(mrb_state *mrb, mrb_value range)
 
   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 */
+  beg = RANGE_BEG(r);
+  end = RANGE_END(r);
+  include_p = r_le(mrb, beg, val) &&                 /* beg <= val */
+              (RANGE_EXCL(r) ? 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:
@@ -290,17 +247,16 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
  *
  * 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_obj_as_string(mrb, RANGE_BEG(r));
+  str2 = mrb_obj_as_string(mrb, RANGE_END(r));
   str  = mrb_str_dup(mrb, str);
-  mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
+  mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
   mrb_str_cat_str(mrb, str, str2);
 
   return str;
@@ -315,17 +271,16 @@ range_to_s(mrb_state *mrb, mrb_value range)
  * <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_inspect(mrb, RANGE_BEG(r));
+  str2 = mrb_inspect(mrb, RANGE_END(r));
   str  = mrb_str_dup(mrb, str);
-  mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
+  mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
   mrb_str_cat_str(mrb, str, str2);
 
   return str;
@@ -343,9 +298,7 @@ range_inspect(mrb_state *mrb, mrb_value range)
  *    (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)
 {
@@ -355,16 +308,14 @@ range_eql(mrb_state *mrb, mrb_value range)
   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, mrb->range_class)) {
-    return mrb_false_value();
-  }
+  if (!mrb_obj_is_kind_of(mrb, obj, mrb->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)) {
+  if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
+      !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
+      (RANGE_EXCL(r) != RANGE_EXCL(o))) {
     return mrb_false_value();
   }
   return mrb_true_value();
@@ -385,7 +336,7 @@ range_initialize_copy(mrb_state *mrb, mrb_value copy)
   }
 
   r = mrb_range_ptr(mrb, src);
-  range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
+  range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
 
   return copy;
 }
@@ -420,6 +371,66 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
 }
 
 void
+mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
+{
+  if (RANGE_INITIALIZED_P(r)) {
+    mrb_gc_mark_value(mrb, RANGE_BEG(r));
+    mrb_gc_mark_value(mrb, RANGE_END(r));
+  }
+}
+
+MRB_API struct RRange*
+mrb_range_ptr(mrb_state *mrb, mrb_value range)
+{
+  struct RRange *r = mrb_range_raw_ptr(range);
+
+  /* check for if #initialize_copy was removed [#3320] */
+  if (!RANGE_INITIALIZED_P(r)) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
+  }
+  return r;
+}
+
+MRB_API mrb_value
+mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+  struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
+  return mrb_range_value(r);
+}
+
+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, RANGE_BEG(r));
+  end = mrb_int(mrb, RANGE_END(r));
+
+  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 (!RANGE_EXCL(r) && (!trunc || end < len)) end++;  /* include end point */
+  len = end - beg;
+  if (len < 0) len = 0;
+
+  *begp = beg;
+  *lenp = len;
+  return 1;
+}
+
+void
 mrb_init_range(mrb_state *mrb)
 {
   struct RClass *r;
@@ -428,17 +439,16 @@ mrb_init_range(mrb_state *mrb)
   mrb->range_class = r;
   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, "begin",           range_beg,             MRB_ARGS_NONE()); /* 15.2.14.4.3  */
+  mrb_define_method(mrb, r, "end",             range_end,             MRB_ARGS_NONE()); /* 15.2.14.4.5  */
+  mrb_define_method(mrb, r, "==",              range_eq,              MRB_ARGS_REQ(1)); /* 15.2.14.4.1  */
+  mrb_define_method(mrb, r, "===",             range_include,         MRB_ARGS_REQ(1)); /* 15.2.14.4.2  */
+  mrb_define_method(mrb, r, "exclude_end?",    range_excl,            MRB_ARGS_NONE()); /* 15.2.14.4.6  */
+  mrb_define_method(mrb, r, "first",           range_beg,             MRB_ARGS_NONE()); /* 15.2.14.4.7  */
+  mrb_define_method(mrb, r, "include?",        range_include,         MRB_ARGS_REQ(1)); /* 15.2.14.4.8  */
+  mrb_define_method(mrb, r, "initialize",      range_initialize,      MRB_ARGS_ANY());  /* 15.2.14.4.9  */
+  mrb_define_method(mrb, r, "last",            range_end,             MRB_ARGS_NONE()); /* 15.2.14.4.10 */
+  mrb_define_method(mrb, r, "member?",         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) */
index 18d1045..c3ce1dc 100644 (file)
@@ -26,6 +26,7 @@ mrb_open_core(mrb_allocf f, void *ud)
   static const struct mrb_context mrb_context_zero = { 0 };
   mrb_state *mrb;
 
+  if (f == NULL) f = mrb_default_allocf;
   mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
   if (mrb == NULL) return NULL;
 
@@ -41,6 +42,10 @@ mrb_open_core(mrb_allocf f, void *ud)
 
   mrb_init_core(mrb);
 
+#if !defined(MRB_DISABLE_STDIO) && defined(_MSC_VER) && _MSC_VER < 1900
+  _set_output_format(_TWO_DIGIT_EXPONENT);
+#endif
+
   return mrb;
 }
 
@@ -168,10 +173,6 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
   }
   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);
 }
@@ -273,7 +274,6 @@ mrb_add_irep(mrb_state *mrb)
   irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
   *irep = mrb_irep_zero;
   irep->refcnt = 1;
-  irep->own_filename = FALSE;
 
   return irep;
 }
index 8ebd11b..63c592d 100644 (file)
@@ -20,6 +20,7 @@
 #include <mruby/class.h>
 #include <mruby/range.h>
 #include <mruby/string.h>
+#include <mruby/numeric.h>
 #include <mruby/re.h>
 
 typedef struct mrb_shared_string {
@@ -156,13 +157,6 @@ 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)
 {
@@ -238,27 +232,36 @@ utf8len(const char* p, const char* e)
   return len;
 }
 
-static mrb_int
-utf8_strlen(mrb_value str, mrb_int len)
+mrb_int
+mrb_utf8_len(const char *str, mrb_int byte_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) {
+  const char *p = str;
+  const char *e = p + byte_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)
+static mrb_int
+utf8_strlen(mrb_value str)
+{
+  mrb_int byte_len = RSTRING_LEN(str);
+
+  if (RSTRING(str)->flags & MRB_STR_NO_UTF) {
+    return byte_len;
+  }
+  else {
+    mrb_int utf8_len = mrb_utf8_len(RSTRING_PTR(str), byte_len);
+    if (byte_len == utf8_len) RSTRING(str)->flags |= MRB_STR_NO_UTF;
+    return utf8_len;
+  }
+}
+
+#define RSTRING_CHAR_LEN(s) utf8_strlen(s)
 
 /* map character index to byte offset index */
 static mrb_int
@@ -708,6 +711,9 @@ mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
   mrb_int slen;
   struct RString *s = mrb_str_ptr(str);
 
+  if (len < 0) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "negative (or overflowed) string size");
+  }
   mrb_str_modify(mrb, s);
   slen = RSTR_LEN(s);
   if (len != slen) {
@@ -736,27 +742,13 @@ mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
   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)
 {
-  if (!mrb_string_p(other)) {
-    other = mrb_str_to_str(mrb, other);
-  }
+  other = mrb_str_to_str(mrb, other);
   mrb_str_cat_str(mrb, self, other);
 }
 
-/*
- *  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)
 {
@@ -774,10 +766,13 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
 /* 15.2.10.5.2  */
 
 /*
- *  call-seq: (Caution! String("abcd") remain) for stack_argument
- *     String("abcdefg") = String("abcd") + String("efg")
+ *  call-seq:
+ *     str + other_str   -> new_str
  *
- *  Returns a new string object containing a copy of <i>str</i>.
+ *  Concatenation---Returns a new <code>String</code> containing
+ *  <i>other_str</i> concatenated to <i>str</i>.
+ *
+ *     "Hello from " + self.to_s   #=> "Hello from main"
  */
 static mrb_value
 mrb_str_plus_m(mrb_state *mrb, mrb_value self)
@@ -953,15 +948,7 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
 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);
-  }
+  if (!mrb_string_p(str2)) return FALSE;
   return str_eql(mrb, str1, str2);
 }
 
@@ -986,33 +973,36 @@ mrb_str_equal_m(mrb_state *mrb, mrb_value str1)
   return mrb_bool_value(mrb_str_equal(mrb, str1, str2));
 }
 /* ---------------------------------- */
+mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass);
+
 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;
+  switch (mrb_type(str)) {
+  case MRB_TT_STRING:
+    return str;
+  case MRB_TT_FIXNUM:
+    return mrb_fixnum_to_str(mrb, str, 10);
+  case MRB_TT_CLASS:
+  case MRB_TT_MODULE:
+    return mrb_mod_to_s(mrb, str);
+  default:
+    return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s");
   }
-  return str;
 }
 
 MRB_API const char*
-mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
+mrb_string_value_ptr(mrb_state *mrb, mrb_value str)
 {
-  mrb_value str = mrb_str_to_str(mrb, ptr);
+  str = mrb_str_to_str(mrb, str);
   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);
+  mrb_to_str(mrb, ptr);
+  return RSTRING_LEN(ptr);
 }
 
 void
@@ -1600,8 +1590,6 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str)
   return mrb_fixnum_value(pos);
 }
 
-#define STR_REPLACE_SHARED_MIN 10
-
 /* 15.2.10.5.24 */
 /* 15.2.10.5.28 */
 /*
@@ -1711,18 +1699,6 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
   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:
@@ -2030,7 +2006,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
   return result;
 }
 
-MRB_API mrb_value
+static mrb_value
 mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, int badcheck)
 {
   const char *p = str;
@@ -2167,10 +2143,13 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
     n *= base;
     n += c;
     if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) {
+#ifndef MRB_WITHOUT_FLOAT
       if (base == 10) {
         return mrb_float_value(mrb, mrb_str_to_dbl(mrb, mrb_str_new(mrb, str, len), badcheck));
       }
-      else {
+      else
+#endif
+      {
         mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer",
                    mrb_str_new(mrb, str, pend-str));
       }
@@ -2195,7 +2174,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
 }
 
 MRB_API mrb_value
-mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
+mrb_cstr_to_inum(mrb_state *mrb, const char *str, mrb_int base, mrb_bool badcheck)
 {
   return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck);
 }
@@ -2203,7 +2182,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
 MRB_API const char*
 mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
 {
-  mrb_value str = mrb_str_to_str(mrb, *ptr);
+  mrb_value str = mrb_to_str(mrb, *ptr);
   struct RString *ps = mrb_str_ptr(str);
   mrb_int len = mrb_str_strlen(mrb, ps);
   char *p = RSTR_PTR(ps);
@@ -2333,7 +2312,7 @@ 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);
+  mrb_to_str(mrb, str);
   s = RSTRING_PTR(str);
   len = RSTRING_LEN(str);
   if (s) {
@@ -2373,7 +2352,6 @@ mrb_str_to_f(mrb_state *mrb, mrb_value self)
 /*
  *  call-seq:
  *     str.to_s     => str
- *     str.to_str   => str
  *
  *  Returns the receiver.
  */
@@ -2612,13 +2590,16 @@ 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)
 {
+  if (mrb_str_ptr(str) == mrb_str_ptr(str2)) {
+    mrb_str_modify(mrb, mrb_str_ptr(str));
+  }
   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);
+  mrb_to_str(mrb, str2);
   return mrb_str_cat_str(mrb, str1, str2);
 }
 
@@ -2863,7 +2844,7 @@ mrb_float_read(const char *string, char **endPtr)
      */
 
     p = string;
-    while (isspace(*p)) {
+    while (ISSPACE(*p)) {
       p += 1;
     }
     if (*p == '-') {
@@ -2886,7 +2867,7 @@ mrb_float_read(const char *string, char **endPtr)
     for (mantSize = 0; ; mantSize += 1)
     {
       c = *p;
-      if (!isdigit(c)) {
+      if (!ISDIGIT(c)) {
         if ((c != '.') || (decPt >= 0)) {
           break;
         }
@@ -2971,7 +2952,7 @@ mrb_float_read(const char *string, char **endPtr)
         }
         expSign = FALSE;
       }
-      while (isdigit(*p)) {
+      while (ISDIGIT(*p)) {
         exp = exp * 10 + (*p - '0');
         if (exp > 19999) {
           exp = 19999;
index 5e1f9f5..96ca9dd 100644 (file)
 /* ------------------------------------------------------ */
 typedef struct symbol_name {
   mrb_bool lit : 1;
+  uint8_t prev;
   uint16_t len;
   const char *name;
 } symbol_name;
 
-static inline khint_t
-sym_hash_func(mrb_state *mrb, mrb_sym s)
+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");
+  }
+}
+
+#ifndef MRB_ENABLE_ALL_SYMBOLS
+static const char pack_table[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+static mrb_sym
+sym_inline_pack(const char *name, uint16_t len)
 {
-  khint_t h = 0;
-  size_t i, len = mrb->symtbl[s].len;
-  const char *p = mrb->symtbl[s].name;
+  const int lower_length_max = (MRB_SYMBOL_BITSIZE - 2) / 5;
+  const int mix_length_max   = (MRB_SYMBOL_BITSIZE - 2) / 6;
 
+  char c;
+  const char *p;
+  int i;
+  mrb_sym sym = 0;
+  int lower = 1;
+
+  if (len > lower_length_max) return 0; /* too long */
   for (i=0; i<len; i++) {
-    h = (h << 5) - h + *p++;
+    uint32_t bits;
+
+    c = name[i];
+    if (c == 0) return 0;       /* NUL in name */
+    p = strchr(pack_table, (int)c);
+    if (p == 0) return 0;       /* non alnum char */
+    bits = (uint32_t)(p - pack_table)+1;
+    if (bits > 27) lower = 0;
+    if (i >= mix_length_max) break;
+    sym |= bits<<(i*6+2);
   }
-  return h;
+  if (lower) {
+    sym = 0;
+    for (i=0; i<len; i++) {
+      uint32_t bits;
+
+      c = name[i];
+      p = strchr(pack_table, (int)c);
+      bits = (uint32_t)(p - pack_table)+1;
+      sym |= bits<<(i*5+2);
+    }
+    return sym | 3;
+  }
+  if (len > mix_length_max) return 0;
+  return sym | 1;
 }
-#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)
+static const char*
+sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp)
 {
-  if (len >= RITE_LV_NULL_MARK) {
-    mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+  int bit_per_char = sym&2 ? 5 : 6;  /* all lower case if `sym&2` is true */
+  int i;
+
+  mrb_assert(sym&1);
+
+  for (i=0; i<30/bit_per_char; i++) {
+    uint32_t bits = sym>>(i*bit_per_char+2) & ((1<<bit_per_char)-1);
+    if (bits == 0) break;
+    buf[i] = pack_table[bits-1];;
   }
+  buf[i] = '\0';
+  if (lenp) *lenp = i;
+  return buf;
+}
+#endif
+
+uint8_t
+symhash(const char *key, size_t len)
+{
+    uint32_t hash, i;
+
+    for(hash = i = 0; i < len; ++i) {
+        hash += key[i];
+        hash += (hash << 10);
+        hash ^= (hash >> 6);
+    }
+    hash += (hash << 3);
+    hash ^= (hash >> 11);
+    hash += (hash << 15);
+    return hash & 0xff;
+}
+
+static mrb_sym
+find_symbol(mrb_state *mrb, const char *name, uint16_t len, uint8_t hash)
+{
+  mrb_sym i;
+  symbol_name *sname;
+
+#ifndef MRB_ENABLE_ALL_SYMBOLS
+  /* inline symbol */
+  i = sym_inline_pack(name, len);
+  if (i > 0) return i;
+#endif
+
+  i = mrb->symhash[hash];
+  if (i == 0) return 0;
+  do {
+    sname = &mrb->symtbl[i];
+    if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+      return i<<1;
+    }
+    if (sname->prev == 0xff) {
+      i -= 0xff;
+      sname = &mrb->symtbl[i];
+      while (mrb->symtbl < sname) {
+        if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+          return (mrb_sym)(sname - mrb->symtbl)<<1;
+        }
+        sname--;
+      }
+      return 0;
+    }
+    i -= sname->prev;
+  } while (sname->prev > 0);
+  return 0;
 }
 
 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;
+  symbol_name *sname;
+  uint8_t hash;
 
   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);
-  }
+  hash = symhash(name, len);
+  sym = find_symbol(mrb, name, len, hash);
+  if (sym > 0) return sym;
 
   /* registering a new symbol */
   sym = ++mrb->symidx;
@@ -78,15 +168,25 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
     sname->lit = TRUE;
   }
   else {
-    p = (char *)mrb_malloc(mrb, len+1);
+    char *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);
+  if (mrb->symhash[hash]) {
+    mrb_sym i = sym - mrb->symhash[hash];
+    if (i > 0xff)
+      sname->prev = 0xff;
+    else
+      sname->prev = i;
+  }
+  else {
+    sname->prev = 0;
+  }
+  mrb->symhash[hash] = sym;
 
-  return sym;
+  return sym<<1;
 }
 
 MRB_API mrb_sym
@@ -116,25 +216,18 @@ mrb_intern_str(mrb_state *mrb, mrb_value 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;
+  mrb_sym sym;
 
   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));
-  }
+  sym = find_symbol(mrb, name, len, symhash(name, len));
+  if (sym > 0) return mrb_symbol_value(sym);
   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));
+  return mrb_check_intern(mrb, name, strlen(name));
 }
 
 MRB_API mrb_value
@@ -143,10 +236,16 @@ 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)
+static const char*
+sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp)
 {
+#ifndef MRB_ENABLE_ALL_SYMBOLS
+  if (sym & 1) {                /* inline packed symbol */
+    return sym_inline_unpack(sym, buf, lenp);
+  }
+#endif
+
+  sym >>= 1;
   if (sym == 0 || mrb->symidx < sym) {
     if (lenp) *lenp = 0;
     return NULL;
@@ -156,6 +255,12 @@ mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
   return mrb->symtbl[sym].name;
 }
 
+MRB_API const char*
+mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
+{
+  return sym2name_len(mrb, sym, mrb->symbuf, lenp);
+}
+
 void
 mrb_free_symtbl(mrb_state *mrb)
 {
@@ -167,13 +272,11 @@ mrb_free_symtbl(mrb_state *mrb)
     }
   }
   mrb_free(mrb, mrb->symtbl);
-  kh_destroy(n2s, mrb, mrb->name2sym);
 }
 
 void
 mrb_init_symtbl(mrb_state *mrb)
 {
-  mrb->name2sym = kh_init(n2s, mrb);
 }
 
 /**********************************************************************
@@ -209,26 +312,6 @@ mrb_init_symtbl(mrb_state *mrb)
  *
  */
 
-
-/* 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  */
 /*
@@ -241,14 +324,9 @@ sym_equal(mrb_state *mrb, mrb_value sym1)
  *     :fred.id2name   #=> "fred"
  */
 static mrb_value
-mrb_sym_to_s(mrb_state *mrb, mrb_value sym)
+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);
+  return mrb_sym2str(mrb, mrb_symbol(sym));
 }
 
 /* 15.2.11.3.4  */
@@ -387,8 +465,8 @@ id:
         switch (*m) {
           case '!': case '?': case '=': ++m;
           default: break;
-            }
         }
+      }
       break;
   }
   return *m ? FALSE : TRUE;
@@ -425,6 +503,9 @@ mrb_sym2str(mrb_state *mrb, mrb_sym sym)
   const char *name = mrb_sym2name_len(mrb, sym, &len);
 
   if (!name) return mrb_undef_value(); /* can't happen */
+  if (sym&1) {                         /* inline symbol */
+    return mrb_str_new(mrb, name, len);
+  }
   return mrb_str_new_static(mrb, name, len);
 }
 
@@ -439,7 +520,14 @@ mrb_sym2name(mrb_state *mrb, mrb_sym sym)
     return name;
   }
   else {
-    mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len));
+    mrb_value str;
+    if (sym&1) {                /* inline symbol */
+      str = mrb_str_new(mrb, name, len);
+    }
+    else {
+      str = mrb_str_new_static(mrb, name, len);
+    }
+    str = mrb_str_dump(mrb, str);
     return RSTRING_PTR(str);
   }
 }
@@ -461,9 +549,10 @@ sym_cmp(mrb_state *mrb, mrb_value s1)
     const char *p1, *p2;
     int retval;
     mrb_int len, len1, len2;
+    char buf1[8], buf2[8];
 
-    p1 = mrb_sym2name_len(mrb, sym1, &len1);
-    p2 = mrb_sym2name_len(mrb, sym2, &len2);
+    p1 = sym2name_len(mrb, sym1, buf1, &len1);
+    p2 = sym2name_len(mrb, sym2, buf2, &len2);
     len = lesser(len1, len2);
     retval = memcmp(p1, p2, len);
     if (retval == 0) {
@@ -481,14 +570,13 @@ 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->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));
+  mrb_define_method(mrb, sym, "id2name", sym_to_s,    MRB_ARGS_NONE());          /* 15.2.11.3.2 */
+  mrb_define_method(mrb, sym, "to_s",    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 de36efa..724b153 100644 (file)
 #include <mruby/class.h>
 #include <mruby/proc.h>
 #include <mruby/string.h>
+#include <mruby/variable.h>
 
-typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
-
-#include <mruby/khash.h>
-
-#ifndef MRB_IVHASH_INIT_SIZE
-#define MRB_IVHASH_INIT_SIZE KHASH_MIN_SIZE
+#ifndef MRB_IV_SEGMENT_SIZE
+#define MRB_IV_SEGMENT_SIZE 4
 #endif
 
-KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE)
-KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal)
+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 {
-  khash_t(iv) h;
+  segment *rootseg;
+  size_t size;
+  size_t last_len;
 } iv_tbl;
 
-/*
- * Creates the instance variable table.
- *
- * Parameters
- *   mrb
- * Returns
- *   the instance variable table.
- */
+/* Creates the instance variable table. */
 static iv_tbl*
 iv_new(mrb_state *mrb)
 {
-  return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE);
+  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.
- */
+/* Set the value for the symbol in the instance variable table. */
 static void
 iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
 {
-  khash_t(iv) *h = &t->h;
-  khiter_t k;
+  segment *seg;
+  segment *prev = NULL;
+  segment *matched_seg = NULL;
+  size_t matched_idx = 0;
+  size_t i;
 
-  k = kh_put(iv, mrb, h, sym);
-  kh_value(h, k) = val;
+  if (t == NULL) return;
+  seg = t->rootseg;
+  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.
- */
+/* Get a value for a symbol from the instance variable table. */
 static mrb_bool
 iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
 {
-  khash_t(iv) *h = &t->h;
-  khiter_t k;
+  segment *seg;
+  size_t i;
 
-  k = kh_get(iv, mrb, h, sym);
-  if (k != kh_end(h)) {
-    if (vp) *vp = kh_value(h, k);
-    return TRUE;
+  if (t == NULL) return FALSE;
+  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.
- */
+/* Deletes the value for the symbol from 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;
+
   if (t == NULL) return FALSE;
-  else {
-    khash_t(iv) *h = &t->h;
-    khiter_t k;
-
-    k = kh_get(iv, mrb, h, sym);
-    if (k != kh_end(h)) {
-      mrb_value val = kh_value(h, k);
-      kh_del(iv, mrb, h, k);
-      if (vp) *vp = val;
-      return TRUE;
+  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)
+/* Iterates over the instance variable table. */
+static void
+iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_iv_foreach_func *func, void *p)
 {
-  if (t == NULL) {
-    return TRUE;
-  }
-  else {
-    khash_t(iv) *h = &t->h;
-    khiter_t k;
-    int n;
-
-    for (k = kh_begin(h); k != kh_end(h); k++) {
-      if (kh_exist(h, k)) {
-        n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p);
-        if (n > 0) return FALSE;
-        if (n < 0) {
-          kh_del(iv, mrb, h, k);
+  segment *seg;
+  size_t i;
+
+  if (t == NULL) return;
+  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;
+      }
+      if (key != 0) {
+        if ((*func)(mrb, key, seg->val[i], p) != 0) {
+          return;
         }
       }
     }
+    seg = seg->next;
   }
-  return TRUE;
+  return;
 }
 
+/* Get the size of the instance variable table. */
 static size_t
 iv_size(mrb_state *mrb, iv_tbl *t)
 {
-  if (t) {
-    return kh_size(&t->h);
+  segment *seg;
+  size_t size = 0;
+
+  if (t == NULL) 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;
 }
 
+/* Copy the instance variable table. */
 static iv_tbl*
 iv_copy(mrb_state *mrb, iv_tbl *t)
 {
-  return (iv_tbl*)kh_copy(iv, mrb, &t->h);
+  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;
 }
 
+/* Free memory of the instance variable table. */
 static void
 iv_free(mrb_state *mrb, iv_tbl *t)
 {
-  kh_destroy(iv, mrb, &t->h);
+  segment *seg;
+
+  seg = t->rootseg;
+  while (seg) {
+    segment *p = seg;
+    seg = seg->next;
+    mrb_free(mrb, p);
+  }
+  mrb_free(mrb, t);
 }
 
 static int
@@ -170,9 +256,7 @@ iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
 static void
 mark_tbl(mrb_state *mrb, iv_tbl *t)
 {
-  if (t) {
-    iv_foreach(mrb, t, iv_mark_i, 0);
-  }
+  iv_foreach(mrb, t, iv_mark_i, 0);
 }
 
 void
@@ -255,19 +339,63 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
   return mrb_nil_value();
 }
 
+static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
+
 MRB_API void
 mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
 {
-  iv_tbl *t = obj->iv;
+  iv_tbl *t;
 
   if (MRB_FROZEN_P(obj)) {
     mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %S", mrb_obj_value(obj));
   }
-  if (!t) {
-    t = obj->iv = iv_new(mrb);
+  assign_class_name(mrb, obj, sym, v);
+  if (!obj->iv) {
+    obj->iv = iv_new(mrb);
   }
-  mrb_write_barrier(mrb, (struct RBasic*)obj);
+  t = obj->iv;
   iv_put(mrb, t, sym, v);
+  mrb_write_barrier(mrb, (struct RBasic*)obj);
+}
+
+/* Iterates over the instance variable table. */
+MRB_API void
+mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p)
+{
+  if (!obj_iv_p(obj)) return;
+  iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p);
+}
+
+static inline mrb_bool
+namespace_p(enum mrb_vtype tt)
+{
+  return tt == MRB_TT_CLASS || tt == MRB_TT_MODULE ? TRUE : FALSE;
+}
+
+static inline void
+assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+{
+  if (namespace_p(obj->tt) && namespace_p(mrb_type(v))) {
+    struct RObject *c = mrb_obj_ptr(v);
+    if (obj != c && ISUPPER(mrb_sym2name(mrb, sym)[0])) {
+      mrb_sym id_classname = mrb_intern_lit(mrb, "__classname__");
+      mrb_value o = mrb_obj_iv_get(mrb, c, id_classname);
+
+      if (mrb_nil_p(o)) {
+        mrb_sym id_outer = mrb_intern_lit(mrb, "__outer__");
+        o = mrb_obj_iv_get(mrb, c, id_outer);
+
+        if (mrb_nil_p(o)) {
+          if ((struct RClass *)obj == mrb->object_class) {
+            mrb_obj_iv_set(mrb, c, id_classname, mrb_symbol_value(sym));
+          }
+          else {
+            mrb_obj_iv_set(mrb, c, id_outer, mrb_obj_value(obj));
+          }
+        }
+      }
+    }
+  }
 }
 
 MRB_API void
@@ -300,28 +428,23 @@ mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
   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)
+mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name)
 {
   const char *s;
-  mrb_int i, len;
+  mrb_int 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;
+  if (ISDIGIT(s[1])) return FALSE;
+  return mrb_ident_p(s+1, len-1);
 }
 
 MRB_API void
-mrb_iv_check(mrb_state *mrb, mrb_sym iv_name)
+mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
 {
-  if (!mrb_iv_p(mrb, iv_name)) {
+  if (!mrb_iv_name_sym_p(mrb, iv_name)) {
     mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name));
   }
 }
@@ -401,27 +524,13 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
     iv_tbl *t = mrb_obj_ptr(obj)->iv;
     mrb_value val;
 
-    if (t && iv_del(mrb, t, sym, &val)) {
+    if (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)
 {
@@ -460,7 +569,7 @@ 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) {
+  if (obj_iv_p(self)) {
     iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary);
   }
   return ary;
@@ -506,15 +615,13 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
   ary = mrb_ary_new(mrb);
   c = mrb_class_ptr(mod);
   while (c) {
-    if (c->iv) {
-      iv_foreach(mrb, c->iv, cv_i, &ary);
-    }
+    iv_foreach(mrb, c->iv, cv_i, &ary);
     c = c->super;
   }
   return ary;
 }
 
-MRB_API mrb_value
+mrb_value
 mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
 {
   struct RClass * cls = c;
@@ -563,14 +670,12 @@ 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;
+    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;
-      }
+    if (iv_get(mrb, t, sym, NULL)) {
+      iv_put(mrb, t, sym, v);
+      mrb_write_barrier(mrb, (struct RBasic*)c);
+      return;
     }
     c = c->super;
   }
@@ -599,8 +704,8 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
     c->iv = iv_new(mrb);
   }
 
-  mrb_write_barrier(mrb, (struct RBasic*)c);
   iv_put(mrb, c->iv, sym, v);
+  mrb_write_barrier(mrb, (struct RBasic*)c);
 }
 
 MRB_API void
@@ -609,14 +714,12 @@ 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_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;
-    }
+    iv_tbl *t = c->iv;
+    if (iv_get(mrb, t, sym, NULL)) return TRUE;
     c = c->super;
   }
 
@@ -662,24 +765,23 @@ mod_const_check(mrb_state *mrb, mrb_value mod)
 }
 
 static mrb_value
-const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool top)
+const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
 {
   struct RClass *c = base;
   mrb_value v;
   mrb_bool retry = FALSE;
   mrb_value name;
-  struct RClass *oclass = mrb->object_class;
 
 L_RETRY:
   while (c) {
-    if (c->iv && (top || c != oclass || base == oclass)) {
+    if (c->iv) {
       if (iv_get(mrb, c->iv, sym, &v))
         return v;
     }
     c = c->super;
   }
   if (!retry && base->tt == MRB_TT_MODULE) {
-    c = oclass;
+    c = mrb->object_class;
     retry = TRUE;
     goto L_RETRY;
   }
@@ -691,7 +793,7 @@ 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, FALSE);
+  return const_get(mrb, mrb_class_ptr(mod), sym);
 }
 
 mrb_value
@@ -703,27 +805,30 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
   struct RProc *proc;
 
   c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
-  if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+  if (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__"));
+
+    if (!iv_get(mrb, c2->iv, mrb_intern_lit(mrb, "__attached__"), &klass)) {
+      c2 = NULL;
+      break;
+    }
     c2 = mrb_class_ptr(klass);
   }
-  if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2;
+  if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
   mrb_assert(!MRB_PROC_CFUNC_P(mrb->c->ci->proc));
   proc = mrb->c->ci->proc;
   while (proc) {
     c2 = MRB_PROC_TARGET_CLASS(proc);
-    if (c2 && c2->iv && iv_get(mrb, c2->iv, sym, &v)) { 
+    if (c2 && iv_get(mrb, c2->iv, sym, &v)) {
       return v;
     }
     proc = proc->upper;
   }
-  return const_get(mrb, c, sym, TRUE);
+  return const_get(mrb, c, sym);
 }
 
 MRB_API void
@@ -796,9 +901,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value 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);
-    }
+    iv_foreach(mrb, c->iv, const_i, &ary);
     if (!inherit) break;
     c = c->super;
     if (c == mrb->object_class) break;
@@ -811,9 +914,6 @@ 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();
@@ -825,20 +925,15 @@ 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;
+    mrb->globals = iv_new(mrb);
   }
+  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);
 }
 
@@ -870,9 +965,7 @@ mrb_f_global_variables(mrb_state *mrb, mrb_value self)
   size_t i;
   char buf[3];
 
-  if (t) {
-    iv_foreach(mrb, t, gv_i, &ary);
-  }
+  iv_foreach(mrb, t, gv_i, &ary);
   buf[0] = '$';
   buf[2] = 0;
   for (i = 1; i <= 9; ++i) {
@@ -892,7 +985,7 @@ mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude,
   tmp = klass;
 retry:
   while (tmp) {
-    if (tmp->iv && iv_get(mrb, tmp->iv, id, NULL)) {
+    if (iv_get(mrb, tmp->iv, id, NULL)) {
       return TRUE;
     }
     if (!recurse && (klass != mrb->object_class)) break;
@@ -928,25 +1021,25 @@ 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;
 }
+
 static mrb_sym
 find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c)
 {
   struct csym_arg arg;
+
   if (!outer) return 0;
   if (outer == c) return 0;
   arg.c = c;
@@ -1009,8 +1102,21 @@ mrb_class_find_path(mrb_state *mrb, struct RClass *c)
 
   str = mrb_sym2name_len(mrb, name, &len);
   mrb_str_cat(mrb, path, str, len);
-  iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL);
-  iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path);
-  mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
+  if (RSTRING_PTR(path)[0] != '#') {
+    iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL);
+    iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path);
+    mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
+  }
   return path;
 }
+
+#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
+
+mrb_bool
+mrb_ident_p(const char *s, mrb_int len)
+{
+  for (mrb_int i = 0; i < len; i++) {
+    if (!identchar(s[i])) return FALSE;
+  }
+  return TRUE;
+}
index 9e43d0a..a381de2 100644 (file)
@@ -55,7 +55,7 @@ void abort(void);
 
 /* Maximum depth of ecall() recursion. */
 #ifndef MRB_ECALL_DEPTH_MAX
-#define MRB_ECALL_DEPTH_MAX 32
+#define MRB_ECALL_DEPTH_MAX 512
 #endif
 
 /* Maximum stack depth. Should be set lower on memory constrained systems.
@@ -101,7 +101,7 @@ static inline void
 stack_clear(mrb_value *from, size_t count)
 {
 #ifndef MRB_NAN_BOXING
-  const mrb_value mrb_value_zero = { 0 };
+  const mrb_value mrb_value_zero = { { 0 } };
 
   while (count-- > 0) {
     *from++ = mrb_value_zero;
@@ -141,7 +141,7 @@ stack_init(mrb_state *mrb)
 }
 
 static inline void
-envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
+envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize)
 {
   mrb_callinfo *ci = mrb->c->cibase;
 
@@ -151,7 +151,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
     mrb_value *st;
 
     if (e && MRB_ENV_STACK_SHARED_P(e) &&
-        (st = e->stack) && oldbase <= st && st < oldbase+size) {
+        (st = e->stack) && oldbase <= st && st < oldbase+oldsize) {
       ptrdiff_t off = e->stack - oldbase;
 
       e->stack = newbase + off;
@@ -161,7 +161,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
       e = MRB_PROC_ENV(ci->proc);
 
       if (e && MRB_ENV_STACK_SHARED_P(e) &&
-          (st = e->stack) && oldbase <= st && st < oldbase+size) {
+          (st = e->stack) && oldbase <= st && st < oldbase+oldsize) {
         ptrdiff_t off = e->stack - oldbase;
 
         e->stack = newbase + off;
@@ -176,7 +176,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
 /** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end  */
 
 static void
-stack_extend_alloc(mrb_state *mrb, int room)
+stack_extend_alloc(mrb_state *mrb, mrb_int room)
 {
   mrb_value *oldbase = mrb->c->stbase;
   mrb_value *newstack;
@@ -186,7 +186,7 @@ stack_extend_alloc(mrb_state *mrb, int room)
 
   if (off > size) size = off;
 #ifdef MRB_STACK_EXTEND_DOUBLING
-  if (room <= size)
+  if ((size_t)room <= size)
     size *= 2;
   else
     size += room;
@@ -205,7 +205,7 @@ stack_extend_alloc(mrb_state *mrb, int room)
     mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
   }
   stack_clear(&(newstack[oldsize]), size - oldsize);
-  envadjust(mrb, oldbase, newstack, size);
+  envadjust(mrb, oldbase, newstack, oldsize);
   mrb->c->stbase = newstack;
   mrb->c->stack = mrb->c->stbase + off;
   mrb->c->stend = mrb->c->stbase + size;
@@ -217,8 +217,8 @@ stack_extend_alloc(mrb_state *mrb, int room)
   }
 }
 
-static inline void
-stack_extend(mrb_state *mrb, int room)
+MRB_API void
+mrb_stack_extend(mrb_state *mrb, mrb_int room)
 {
   if (mrb->c->stack + room >= mrb->c->stend) {
     stack_extend_alloc(mrb, room);
@@ -300,6 +300,7 @@ mrb_env_unshare(mrb_state *mrb, struct REnv *e)
 
     if (!MRB_ENV_STACK_SHARED_P(e)) return;
     if (e->cxt != mrb->c) return;
+    if (e == mrb->c->cibase->env) return; /* for mirb */
     p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
     if (len > 0) {
       stack_copy(p, e->stack, len);
@@ -332,11 +333,12 @@ ecall(mrb_state *mrb)
   struct REnv *env;
   ptrdiff_t cioff;
   int ai = mrb_gc_arena_save(mrb);
-  int i = --c->eidx;
+  uint16_t i = --c->eidx;
   int nregs;
 
   if (i<0) return;
-  if (ci - c->cibase > MRB_ECALL_DEPTH_MAX) {
+  /* restrict total call depth of ecall() */
+  if (++mrb->ecall_nest > MRB_ECALL_DEPTH_MAX) {
     mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
   }
   p = c->ensure[i];
@@ -355,7 +357,6 @@ ecall(mrb_state *mrb)
   ci->acc = CI_ACC_SKIP;
   ci->argc = 0;
   ci->proc = p;
-  ci->nregs = p->body.irep->nregs;
   ci->target_class = MRB_PROC_TARGET_CLASS(p);
   env = MRB_PROC_ENV(p);
   mrb_assert(env);
@@ -364,11 +365,15 @@ ecall(mrb_state *mrb)
   if (exc) {
     mrb_gc_protect(mrb, mrb_obj_value(exc));
   }
+  if (mrb->c->fib) {
+    mrb_gc_protect(mrb, mrb_obj_value(mrb->c->fib));
+  }
   mrb_run(mrb, p, env->stack[0]);
   mrb->c = c;
   c->ci = c->cibase + cioff;
   if (!mrb->exc) mrb->exc = exc;
   mrb_gc_arena_restore(mrb, ai);
+  mrb->ecall_nest--;
 }
 
 #ifndef MRB_FUNCALL_ARGC_MAX
@@ -395,6 +400,30 @@ mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...)
   return mrb_funcall_argv(mrb, self, mid, argc, argv);
 }
 
+static int
+ci_nregs(mrb_callinfo *ci)
+{
+  struct RProc *p;
+  int n = 0;
+
+  if (!ci) return 3;
+  p = ci->proc;
+  if (!p) {
+    if (ci->argc < 0) return 3;
+    return ci->argc+2;
+  }
+  if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
+    n = p->body.irep->nregs;
+  }
+  if (ci->argc < 0) {
+    if (n < 3) n = 3; /* self + args + blk */
+  }
+  if (ci->argc > n) {
+    n = ci->argc + 2; /* self + blk */
+  }
+  return n;
+}
+
 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)
 {
@@ -425,13 +454,12 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
     mrb_method_t m;
     struct RClass *c;
     mrb_callinfo *ci;
-    int n;
+    int n = ci_nregs(mrb->c->ci);
     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));
     }
@@ -445,7 +473,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
         mrb_method_missing(mrb, mid, self, args);
       }
       mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
-      stack_extend(mrb, n+2);
+      mrb_stack_extend(mrb, n+2);
       mrb->c->stack[n+1] = args;
       argc = -1;
     }
@@ -462,22 +490,22 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
       voff = argv - mrb->c->stbase;
     }
     if (MRB_METHOD_CFUNC_P(m)) {
-      ci->nregs = (int)(argc + 2);
-      stack_extend(mrb, ci->nregs);
+      mrb_stack_extend(mrb, argc + 2);
     }
     else if (argc >= CALL_MAXARGS) {
       mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
-      stack_extend(mrb, ci->nregs+2);
+
+      mrb_stack_extend(mrb, 3);
       mrb->c->stack[1] = args;
       ci->argc = -1;
       argc = 1;
     }
     else {
       struct RProc *p = MRB_METHOD_PROC(m);
+
       ci->proc = p;
       if (argc < 0) argc = 1;
-      ci->nregs = (int)(p->body.irep->nregs + argc);
-      stack_extend(mrb, ci->nregs);
+      mrb_stack_extend(mrb, p->body.irep->nregs + argc);
     }
     if (voff >= 0) {
       argv = mrb->c->stbase + voff;
@@ -519,26 +547,25 @@ mrb_value
 mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
 {
   mrb_callinfo *ci = mrb->c->ci;
-  int keep;
+  int keep, nregs;
 
   mrb->c->stack[0] = self;
   ci->proc = p;
   if (MRB_PROC_CFUNC_P(p)) {
     return MRB_PROC_CFUNC(p)(mrb, self);
   }
-  ci->nregs = p->body.irep->nregs;
+  nregs = p->body.irep->nregs;
   if (ci->argc < 0) keep = 3;
   else keep = ci->argc + 2;
-  if (ci->nregs < keep) {
-    stack_extend(mrb, keep);
+  if (nregs < keep) {
+    mrb_stack_extend(mrb, keep);
   }
   else {
-    stack_extend(mrb, ci->nregs);
-    stack_clear(mrb->c->stack+keep, ci->nregs-keep);
+    mrb_stack_extend(mrb, nregs);
+    stack_clear(mrb->c->stack+keep, nregs-keep);
   }
 
   ci = cipush(mrb);
-  ci->nregs = 0;
   ci->target_class = 0;
   ci->pc = p->body.irep->iseq;
   ci->stackent = mrb->c->stack;
@@ -566,7 +593,7 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
  *     k = Klass.new
  *     k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"
  */
-MRB_API mrb_value
+mrb_value
 mrb_f_send(mrb_state *mrb, mrb_value self)
 {
   mrb_sym name;
@@ -617,6 +644,7 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
 {
   struct RProc *p;
   mrb_callinfo *ci;
+  int nregs;
 
   if (mrb_nil_p(blk)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
@@ -632,19 +660,19 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
   ci->argc = 1;
   ci->mid = ci[-1].mid;
   if (MRB_PROC_CFUNC_P(p)) {
-    stack_extend(mrb, 3);
+    mrb_stack_extend(mrb, 3);
     mrb->c->stack[0] = self;
     mrb->c->stack[1] = self;
     mrb->c->stack[2] = mrb_nil_value();
     return MRB_PROC_CFUNC(p)(mrb, self);
   }
-  ci->nregs = p->body.irep->nregs;
-  stack_extend(mrb, (ci->nregs < 3) ? 3 : ci->nregs);
+  nregs = p->body.irep->nregs;
+  if (nregs < 3) nregs = 3;
+  mrb_stack_extend(mrb, nregs);
   mrb->c->stack[0] = self;
   mrb->c->stack[1] = self;
-  mrb->c->stack[2] = mrb_nil_value();
+  stack_clear(mrb->c->stack+2, nregs-2);
   ci = cipush(mrb);
-  ci->nregs = 0;
   ci->target_class = 0;
   ci->pc = p->body.irep->iseq;
   ci->stackent = mrb->c->stack;
@@ -727,13 +755,15 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
   struct RProc *p;
   mrb_sym mid = mrb->c->ci->mid;
   mrb_callinfo *ci;
-  int n = mrb->c->ci->nregs;
   mrb_value val;
+  int n;
 
   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) {
+  ci = mrb->c->ci;
+  n = ci_nregs(ci);
+  if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
     mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
   }
   p = mrb_proc_ptr(b);
@@ -744,9 +774,9 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
   ci->argc = (int)argc;
   ci->target_class = c;
   ci->acc = CI_ACC_SKIP;
+  n = MRB_PROC_CFUNC_P(p) ? (int)(argc+2) : p->body.irep->nregs;
   mrb->c->stack = mrb->c->stack + n;
-  ci->nregs = MRB_PROC_CFUNC_P(p) ? (int)(argc+2) : p->body.irep->nregs;
-  stack_extend(mrb, ci->nregs);
+  mrb_stack_extend(mrb, n);
 
   mrb->c->stack[0] = self;
   if (argc > 0) {
@@ -797,7 +827,7 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const
   p = mrb_proc_ptr(b);
   ci = mrb->c->ci;
 
-  stack_extend(mrb, 3);
+  mrb_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;
@@ -889,8 +919,8 @@ argnum_error(mrb_state *mrb, mrb_int num)
   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;
+#define ERR_PC_SET(mrb) mrb->c->ci->err = pc0;
+#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
@@ -903,25 +933,26 @@ argnum_error(mrb_state *mrb, mrb_int num)
 #define BYTECODE_DECODER(x) (x)
 #endif
 
-
+#ifndef MRB_DISABLE_DIRECT_THREADING
 #if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER
 #define DIRECT_THREADED
 #endif
+#endif /* ifndef MRB_DISABLE_DIRECT_THREADING */
 
 #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 INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) {
+#define CASE(insn,ops) case insn: pc0=pc++; FETCH_ ## ops ();; L_ ## insn ## _BODY:
+#define NEXT break
+#define JUMP NEXT
 #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 CASE(insn,ops) L_ ## insn: pc0=pc++; FETCH_ ## ops (); L_ ## insn ## _BODY:
+#define NEXT insn=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[insn]
+#define JUMP NEXT
 
 #define END_DISPATCH
 
@@ -941,51 +972,57 @@ mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stac
   }
   if (stack_keep > nregs)
     nregs = stack_keep;
-  stack_extend(mrb, nregs);
+  mrb_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) {
+  if (mrb->c != c) {
+    if (mrb->c->fib) {
+      mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+    }
+    mrb->c = c;
+  }
+  else if (c->ci - c->cibase > cioff) {
     c->ci = c->cibase + cioff;
   }
-  mrb->c = c;
   return result;
 }
 
+static mrb_bool
+check_target_class(mrb_state *mrb)
+{
+  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);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
+
 MRB_API mrb_value
 mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
 {
   /* mrb_assert(mrb_proc_cfunc_p(proc)) */
+  mrb_code *pc0 = pc;
   mrb_irep *irep = proc->body.irep;
   mrb_value *pool = irep->pool;
   mrb_sym *syms = irep->syms;
-  mrb_code i;
+  mrb_code insn;
   int ai = mrb_gc_arena_save(mrb);
   struct mrb_jmpbuf *prev_jmp = mrb->jmp;
   struct mrb_jmpbuf c_jmp;
+  uint32_t a;
+  uint16_t b;
+  uint8_t c;
+  mrb_sym mid;
 
 #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,
+#define OPCODE(x,_) &&L_OP_ ## x,
+#include "mruby/ops.h"
+#undef OPCODE
   };
 #endif
 
@@ -996,35 +1033,29 @@ RETRY_TRY_BLOCK:
 
   if (exc_catched) {
     exc_catched = FALSE;
+    mrb_gc_arena_restore(mrb, ai);
     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) {
+    CASE(OP_NOP, Z) {
       /* do nothing */
       NEXT;
     }
 
-    CASE(OP_MOVE) {
-      /* A B    R(A) := R(B) */
-      int a = GETARG_A(i);
-      int b = GETARG_B(i);
+    CASE(OP_MOVE, BB) {
       regs[a] = regs[b];
       NEXT;
     }
 
-    CASE(OP_LOADL) {
-      /* A Bx   R(A) := Pool(Bx) */
-      int a = GETARG_A(i);
-      int bx = GETARG_Bx(i);
+    CASE(OP_LOADL, BB) {
 #ifdef MRB_WORD_BOXING
-      mrb_value val = pool[bx];
+      mrb_value val = pool[b];
 #ifndef MRB_WITHOUT_FLOAT
       if (mrb_float_p(val)) {
         val = mrb_float_value(mrb, mrb_float(val));
@@ -1032,167 +1063,138 @@ RETRY_TRY_BLOCK:
 #endif
       regs[a] = val;
 #else
-      regs[a] = pool[bx];
+      regs[a] = pool[b];
 #endif
       NEXT;
     }
 
-    CASE(OP_LOADI) {
-      /* A sBx  R(A) := sBx */
-      int a = GETARG_A(i);
-      mrb_int bx = GETARG_sBx(i);
-      SET_INT_VALUE(regs[a], bx);
+    CASE(OP_LOADI, BB) {
+      SET_INT_VALUE(regs[a], b);
+      NEXT;
+    }
+
+    CASE(OP_LOADINEG, BB) {
+      SET_INT_VALUE(regs[a], -b);
+      NEXT;
+    }
+
+    CASE(OP_LOADI__1,B) goto L_LOADI;
+    CASE(OP_LOADI_0,B) goto L_LOADI;
+    CASE(OP_LOADI_1,B) goto L_LOADI;
+    CASE(OP_LOADI_2,B) goto L_LOADI;
+    CASE(OP_LOADI_3,B) goto L_LOADI;
+    CASE(OP_LOADI_4,B) goto L_LOADI;
+    CASE(OP_LOADI_5,B) goto L_LOADI;
+    CASE(OP_LOADI_6,B) goto L_LOADI;
+    CASE(OP_LOADI_7, B) {
+    L_LOADI:
+      SET_INT_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0);
       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]);
+    CASE(OP_LOADSYM, BB) {
+      SET_SYM_VALUE(regs[a], syms[b]);
       NEXT;
     }
 
-    CASE(OP_LOADSELF) {
-      /* A      R(A) := self */
-      int a = GETARG_A(i);
+    CASE(OP_LOADNIL, B) {
+      SET_NIL_VALUE(regs[a]);
+      NEXT;
+    }
+
+    CASE(OP_LOADSELF, B) {
       regs[a] = regs[0];
       NEXT;
     }
 
-    CASE(OP_LOADT) {
-      /* A      R(A) := true */
-      int a = GETARG_A(i);
+    CASE(OP_LOADT, B) {
       SET_TRUE_VALUE(regs[a]);
       NEXT;
     }
 
-    CASE(OP_LOADF) {
-      /* A      R(A) := false */
-      int a = GETARG_A(i);
+    CASE(OP_LOADF, B) {
       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]);
+    CASE(OP_GETGV, BB) {
+      mrb_value val = mrb_gv_get(mrb, syms[b]);
       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]);
+    CASE(OP_SETGV, BB) {
+      mrb_gv_set(mrb, syms[b], 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);
+    CASE(OP_GETSV, BB) {
+      mrb_value val = mrb_vm_special_get(mrb, b);
       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]);
+    CASE(OP_SETSV, BB) {
+      mrb_vm_special_set(mrb, b, 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;
+    CASE(OP_GETIV, BB) {
+      regs[a] = mrb_iv_get(mrb, regs[0], syms[b]);
       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]);
+    CASE(OP_SETIV, BB) {
+      mrb_iv_set(mrb, regs[0], syms[b], regs[a]);
       NEXT;
     }
 
-    CASE(OP_GETCV) {
-      /* A Bx   R(A) := cvget(Syms(Bx)) */
-      int a = GETARG_A(i);
-      int bx = GETARG_Bx(i);
+    CASE(OP_GETCV, BB) {
       mrb_value val;
-      ERR_PC_SET(mrb, pc);
-      val = mrb_vm_cv_get(mrb, syms[bx]);
+      ERR_PC_SET(mrb);
+      val = mrb_vm_cv_get(mrb, syms[b]);
       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]);
+    CASE(OP_SETCV, BB) {
+      mrb_vm_cv_set(mrb, syms[b], regs[a]);
       NEXT;
     }
 
-    CASE(OP_GETCONST) {
-      /* A Bx    R(A) := constget(Syms(Bx)) */
+    CASE(OP_GETCONST, BB) {
       mrb_value val;
-      int a = GETARG_A(i);
-      int bx = GETARG_Bx(i);
-      mrb_sym sym = syms[bx];
+      mrb_sym sym = syms[b];
 
-      ERR_PC_SET(mrb, pc);
+      ERR_PC_SET(mrb);
       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]);
+    CASE(OP_SETCONST, BB) {
+      mrb_vm_const_set(mrb, syms[b], regs[a]);
       NEXT;
     }
 
-    CASE(OP_GETMCNST) {
-      /* A Bx   R(A) := R(A)::Syms(Bx) */
+    CASE(OP_GETMCNST, BB) {
       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_SET(mrb);
+      val = mrb_const_get(mrb, regs[a], syms[b]);
       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]);
+    CASE(OP_SETMCNST, BB) {
+      mrb_const_set(mrb, regs[a+1], syms[b], 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);
+    CASE(OP_GETUPVAR, BBB) {
       mrb_value *regs_a = regs + a;
       struct REnv *e = uvenv(mrb, c);
 
@@ -1205,12 +1207,7 @@ RETRY_TRY_BLOCK:
       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);
-
+    CASE(OP_SETUPVAR, BBB) {
       struct REnv *e = uvenv(mrb, c);
 
       if (e) {
@@ -1224,127 +1221,126 @@ RETRY_TRY_BLOCK:
       NEXT;
     }
 
-    CASE(OP_JMP) {
-      /* sBx    pc+=sBx */
-      int sbx = GETARG_sBx(i);
-      pc += sbx;
+    CASE(OP_JMP, S) {
+      pc = irep->iseq+a;
       JUMP;
     }
-
-    CASE(OP_JMPIF) {
-      /* A sBx  if R(A) pc+=sBx */
-      int a = GETARG_A(i);
-      int sbx = GETARG_sBx(i);
+    CASE(OP_JMPIF, BS) {
       if (mrb_test(regs[a])) {
-        pc += sbx;
+        pc = irep->iseq+b;
         JUMP;
       }
       NEXT;
     }
-
-    CASE(OP_JMPNOT) {
-      /* A sBx  if !R(A) pc+=sBx */
-      int a = GETARG_A(i);
-      int sbx = GETARG_sBx(i);
+    CASE(OP_JMPNOT, BS) {
       if (!mrb_test(regs[a])) {
-        pc += sbx;
+        pc = irep->iseq+b;
+        JUMP;
+      }
+      NEXT;
+    }
+    CASE(OP_JMPNIL, BS) {
+      if (mrb_nil_p(regs[a])) {
+        pc = irep->iseq+b;
         JUMP;
       }
       NEXT;
     }
 
-    CASE(OP_ONERR) {
-      /* sBx    pc+=sBx on exception */
-      int sbx = GETARG_sBx(i);
+    CASE(OP_ONERR, S) {
+      /* check rescue stack */
+      if (mrb->c->ci->ridx == UINT16_MAX-1) {
+        mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested rescues");
+        mrb_exc_set(mrb, exc);
+        goto L_RAISE;
+      }
+      /* expand rescue stack */
       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);
+        else {
+          mrb->c->rsize *= 2;
+          if (mrb->c->rsize <= mrb->c->ci->ridx) {
+            mrb->c->rsize = UINT16_MAX;
+          }
+        }
+        mrb->c->rescue = (uint16_t*)mrb_realloc(mrb, mrb->c->rescue, sizeof(uint16_t)*mrb->c->rsize);
       }
-      mrb->c->rescue[mrb->c->ci->ridx++] = pc + sbx;
+      /* push rescue stack */
+      mrb->c->rescue[mrb->c->ci->ridx++] = a;
       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;
+    CASE(OP_EXCEPT, B) {
+      mrb_value exc = mrb_obj_value(mrb->exc);
+      mrb->exc = 0;
+      regs[a] = exc;
+      NEXT;
+    }
+    CASE(OP_RESCUE, BB) {
+      mrb_value exc = regs[a];  /* exc on stack */
+      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;
+      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;
-          }
+          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;
       }
+      ec = mrb_class_ptr(e);
+      regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec));
       NEXT;
     }
 
-    CASE(OP_POPERR) {
-      /* A      A.times{rescue_pop()} */
-      int a = GETARG_A(i);
-
+    CASE(OP_POPERR, B) {
       mrb->c->ci->ridx -= a;
       NEXT;
     }
 
-    CASE(OP_RAISE) {
-      /* A      raise(R(A)) */
-      int a = GETARG_A(i);
-
+    CASE(OP_RAISE, B) {
       mrb_exc_set(mrb, regs[a]);
       goto L_RAISE;
     }
 
-    CASE(OP_EPUSH) {
-      /* Bx     ensure_push(SEQ[Bx]) */
-      int bx = GETARG_Bx(i);
+    CASE(OP_EPUSH, B) {
       struct RProc *p;
 
-      p = mrb_closure_new(mrb, irep->reps[bx]);
-      /* push ensure_stack */
+      p = mrb_closure_new(mrb, irep->reps[a]);
+      /* check ensure stack */
+      if (mrb->c->eidx == UINT16_MAX-1) {
+        mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested ensures");
+        mrb_exc_set(mrb, exc);
+        goto L_RAISE;
+      }
+      /* expand 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);
+        else {
+          mrb->c->esize *= 2;
+          if (mrb->c->esize <= mrb->c->eidx) {
+            mrb->c->esize = UINT16_MAX;
+          }
+        }
+        mrb->c->ensure = (struct RProc**)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*)*mrb->c->esize);
       }
+      /* push ensure stack */
       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);
+    CASE(OP_EPOP, B) {
       mrb_callinfo *ci = mrb->c->ci;
-      int n, epos = ci->epos;
+      unsigned int n, epos = ci->epos;
       mrb_value self = regs[0];
       struct RClass *target_class = ci->target_class;
 
@@ -1352,10 +1348,11 @@ RETRY_TRY_BLOCK:
         NEXT;
       }
 
-      if (a > mrb->c->eidx - epos)
+      if (a > (int)mrb->c->eidx - epos)
         a = mrb->c->eidx - epos;
-      pc = pc + 1;
       for (n=0; n<a; n++) {
+        int nregs = irep->nregs;
+
         proc = mrb->c->ensure[epos+n];
         mrb->c->ensure[epos+n] = NULL;
         if (proc == NULL) continue;
@@ -1365,12 +1362,11 @@ RETRY_TRY_BLOCK:
         ci->argc = 0;
         ci->proc = proc;
         ci->stackent = mrb->c->stack;
-        ci->nregs = irep->nregs;
         ci->target_class = target_class;
         ci->pc = pc;
-        ci->acc = ci[-1].nregs;
+        ci->acc = nregs;
         mrb->c->stack += ci->acc;
-        stack_extend(mrb, ci->nregs);
+        mrb_stack_extend(mrb, irep->nregs);
         regs[0] = self;
         pc = irep->iseq;
       }
@@ -1380,63 +1376,69 @@ RETRY_TRY_BLOCK:
       JUMP;
     }
 
-    CASE(OP_LOADNIL) {
-      /* A     R(A) := nil */
-      int a = GETARG_A(i);
+    CASE(OP_SENDV, BB) {
+      c = CALL_MAXARGS;
+      goto L_SEND;
+    };
 
-      SET_NIL_VALUE(regs[a]);
-      NEXT;
-    }
+    CASE(OP_SENDVB, BB) {
+      c = CALL_MAXARGS;
+      goto L_SENDB;
+    };
 
-    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 */
+    CASE(OP_SEND, BBB)
+    L_SEND:
+    {
+      /* push nil after arguments */
+      int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1;
+      SET_NIL_VALUE(regs[bidx]);
+      goto L_SENDB;
+    };
+    L_SEND_SYM:
+    {
+      /* push nil after arguments */
+      int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1;
+      SET_NIL_VALUE(regs[bidx]);
+      goto L_SENDB_SYM;
     };
 
-  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);
-      int argc = (n == CALL_MAXARGS) ? -1 : n;
-      int bidx = (argc < 0) ? a+2 : a+n+1;
+    CASE(OP_SENDB, BBB)
+    L_SENDB:
+    mid = syms[b];
+    L_SENDB_SYM:
+    {
+      int argc = (c == CALL_MAXARGS) ? -1 : c;
+      int bidx = (argc < 0) ? a+2 : a+c+1;
       mrb_method_t m;
-      struct RClass *c;
+      struct RClass *cls;
       mrb_callinfo *ci = mrb->c->ci;
       mrb_value recv, blk;
-      mrb_sym mid = syms[GETARG_B(i)];
 
-      mrb_assert(bidx < ci->nregs);
+      mrb_assert(bidx < irep->nregs);
 
       recv = regs[a];
-      if (GET_OPCODE(i) != OP_SENDB) {
-        SET_NIL_VALUE(regs[bidx]);
-        blk = regs[bidx];
-      }
-      else {
-        blk = regs[bidx];
-        if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
-          blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
-          /* The stack might have been reallocated during mrb_convert_type(),
-             see #3622 */
-          regs[bidx] = blk;
-        }
+      blk = regs[bidx];
+      if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
+        blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
+        /* The stack might have been reallocated during mrb_convert_type(),
+           see #3622 */
+        regs[bidx] = blk;
       }
-      c = mrb_class(mrb, recv);
-      m = mrb_method_search_vm(mrb, &c, mid);
+      cls = mrb_class(mrb, recv);
+      m = mrb_method_search_vm(mrb, &cls, mid);
       if (MRB_METHOD_UNDEF_P(m)) {
         mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
-        m = mrb_method_search_vm(mrb, &c, missing);
+        m = mrb_method_search_vm(mrb, &cls, missing);
         if (MRB_METHOD_UNDEF_P(m) || (missing == mrb->c->ci->mid && mrb_obj_eq(mrb, regs[0], recv))) {
-          mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1);
-          ERR_PC_SET(mrb, pc);
+          mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, c, regs+a+1);
+          ERR_PC_SET(mrb);
           mrb_method_missing(mrb, mid, recv, args);
         }
         if (argc >= 0) {
           if (a+2 >= irep->nregs) {
-            stack_extend(mrb, a+3);
+            mrb_stack_extend(mrb, a+3);
           }
-          regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+          regs[a+1] = mrb_ary_new_from_values(mrb, c, regs+a+1);
           regs[a+2] = blk;
           argc = -1;
         }
@@ -1448,17 +1450,16 @@ RETRY_TRY_BLOCK:
       ci = cipush(mrb);
       ci->mid = mid;
       ci->stackent = mrb->c->stack;
-      ci->target_class = c;
+      ci->target_class = cls;
       ci->argc = argc;
 
-      ci->pc = pc + 1;
+      ci->pc = pc;
       ci->acc = a;
 
       /* prepare stack */
       mrb->c->stack += a;
 
       if (MRB_METHOD_CFUNC_P(m)) {
-        ci->nregs = (argc < 0) ? 3 : n+2;
         if (MRB_METHOD_PROC_P(m)) {
           struct RProc *p = MRB_METHOD_PROC(m);
 
@@ -1472,12 +1473,10 @@ RETRY_TRY_BLOCK:
         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) && MRB_PROC_ENV(p) == ci[-1].env) {
-              p->flags |= MRB_PROC_ORPHAN;
-            }
+        if (mrb_type(blk) == MRB_TT_PROC) {
+          struct RProc *p = mrb_proc_ptr(blk);
+          if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) {
+            p->flags |= MRB_PROC_ORPHAN;
           }
         }
         if (!ci->target_class) { /* return from context modifying method (resume/yield) */
@@ -1506,21 +1505,13 @@ RETRY_TRY_BLOCK:
         irep = proc->body.irep;
         pool = irep->pool;
         syms = irep->syms;
-        ci->nregs = irep->nregs;
-        stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs);
+        mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : 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) */
+    CASE(OP_CALL, Z) {
       mrb_callinfo *ci;
       mrb_value recv = mrb->c->stack[0];
       struct RProc *m = mrb_proc_ptr(recv);
@@ -1563,12 +1554,13 @@ RETRY_TRY_BLOCK:
         irep = m->body.irep;
         if (!irep) {
           mrb->c->stack[0] = mrb_nil_value();
-          goto L_RETURN;
+          a = 0;
+          c = OP_R_NORMAL;
+          goto L_OP_RETURN_BODY;
         }
         pool = irep->pool;
         syms = irep->syms;
-        ci->nregs = irep->nregs;
-        stack_extend(mrb, ci->nregs);
+        mrb_stack_extend(mrb, irep->nregs);
         if (ci->argc < 0) {
           if (irep->nregs > 3) {
             stack_clear(regs+3, irep->nregs-3);
@@ -1585,20 +1577,17 @@ RETRY_TRY_BLOCK:
       }
     }
 
-    CASE(OP_SUPER) {
-      /* A C  R(A) := super(R(A+1),... ,R(A+C+1)) */
-      int a = GETARG_A(i);
-      int n = GETARG_C(i);
-      int argc = (n == CALL_MAXARGS) ? -1 : n;
-      int bidx = (argc < 0) ? a+2 : a+n+1;
+    CASE(OP_SUPER, BB) {
+      int argc = (b == CALL_MAXARGS) ? -1 : b;
+      int bidx = (argc < 0) ? a+2 : a+b+1;
       mrb_method_t m;
-      struct RClass *c;
+      struct RClass *cls;
       mrb_callinfo *ci = mrb->c->ci;
       mrb_value recv, blk;
       mrb_sym mid = ci->mid;
       struct RClass* target_class = MRB_PROC_TARGET_CLASS(ci->proc);
 
-      mrb_assert(bidx < ci->nregs);
+      mrb_assert(bidx < irep->nregs);
 
       if (mid == 0 || !target_class) {
         mrb_value exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
@@ -1628,26 +1617,26 @@ RETRY_TRY_BLOCK:
         regs[bidx] = blk;
         ci = mrb->c->ci;
       }
-      c = target_class->super;
-      m = mrb_method_search_vm(mrb, &c, mid);
+      cls = target_class->super;
+      m = mrb_method_search_vm(mrb, &cls, mid);
       if (MRB_METHOD_UNDEF_P(m)) {
         mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
 
         if (mid != missing) {
-          c = mrb_class(mrb, recv);
+          cls = mrb_class(mrb, recv);
         }
-        m = mrb_method_search_vm(mrb, &c, missing);
+        m = mrb_method_search_vm(mrb, &cls, missing);
         if (MRB_METHOD_UNDEF_P(m)) {
-          mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1);
-          ERR_PC_SET(mrb, pc);
+          mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, b, regs+a+1);
+          ERR_PC_SET(mrb);
           mrb_method_missing(mrb, mid, recv, args);
         }
         mid = missing;
         if (argc >= 0) {
-          if (a+2 >= ci->nregs) {
-            stack_extend(mrb, a+3);
+          if (a+2 >= irep->nregs) {
+            mrb_stack_extend(mrb, a+3);
           }
-          regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+          regs[a+1] = mrb_ary_new_from_values(mrb, b, regs+a+1);
           regs[a+2] = blk;
           argc = -1;
         }
@@ -1658,8 +1647,8 @@ RETRY_TRY_BLOCK:
       ci = cipush(mrb);
       ci->mid = mid;
       ci->stackent = mrb->c->stack;
-      ci->target_class = c;
-      ci->pc = pc + 1;
+      ci->target_class = cls;
+      ci->pc = pc;
       ci->argc = argc;
 
       /* prepare stack */
@@ -1668,7 +1657,7 @@ RETRY_TRY_BLOCK:
 
       if (MRB_METHOD_CFUNC_P(m)) {
         mrb_value v;
-        ci->nregs = (argc < 0) ? 3 : n+2;
+
         if (MRB_METHOD_PROC_P(m)) {
           ci->proc = MRB_METHOD_PROC(m);
         }
@@ -1705,21 +1694,18 @@ RETRY_TRY_BLOCK:
         irep = proc->body.irep;
         pool = irep->pool;
         syms = irep->syms;
-        ci->nregs = irep->nregs;
-        stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs);
+        mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : 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;
+    CASE(OP_ARGARY, BS) {
+      int m1 = (b>>11)&0x3f;
+      int r  = (b>>10)&0x1;
+      int m2 = (b>>5)&0x1f;
+      int kd = (b>>4)&0x1;
+      int lv = (b>>0)&0xf;
       mrb_value *stack;
 
       if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) {
@@ -1734,12 +1720,12 @@ RETRY_TRY_BLOCK:
       else {
         struct REnv *e = uvenv(mrb, lv-1);
         if (!e) goto L_NOSUPER;
-        if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1)
+        if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+kd+1)
           goto L_NOSUPER;
         stack = e->stack + 1;
       }
       if (r == 0) {
-        regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack);
+        regs[a] = mrb_ary_new_from_values(mrb, m1+m2+kd, stack);
       }
       else {
         mrb_value *pp = NULL;
@@ -1752,7 +1738,7 @@ RETRY_TRY_BLOCK:
           pp = ARY_PTR(ary);
           len = (int)ARY_LEN(ary);
         }
-        regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
+        regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd);
         rest = mrb_ary_ptr(regs[a]);
         if (m1 > 0) {
           stack_copy(ARY_PTR(rest), stack, m1);
@@ -1763,89 +1749,126 @@ RETRY_TRY_BLOCK:
         if (m2 > 0) {
           stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2);
         }
-        ARY_SET_LEN(rest, m1+len+m2);
+        if (kd) {
+          stack_copy(ARY_PTR(rest)+m1+len+m2, stack+m1+m2+1, kd);
+        }
+        ARY_SET_LEN(rest, m1+len+m2+kd);
       }
       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);
+    CASE(OP_ENTER, W) {
+      int m1 = MRB_ASPEC_REQ(a);
+      int o  = MRB_ASPEC_OPT(a);
+      int r  = MRB_ASPEC_REST(a);
+      int m2 = MRB_ASPEC_POST(a);
+      int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0;
       /* unused
-      int k  = MRB_ASPEC_KEY(ax);
-      int kd = MRB_ASPEC_KDICT(ax);
-      int b  = MRB_ASPEC_BLOCK(ax);
+      int b  = MRB_ASPEC_BLOCK(a);
       */
       int argc = mrb->c->ci->argc;
       mrb_value *argv = regs+1;
-      mrb_value *argv0 = argv;
-      int len = m1 + o + r + m2;
+      mrb_value * const argv0 = argv;
+      int const len = m1 + o + r + m2;
+      int const blk_pos = len + kd + 1;
       mrb_value *blk = &argv[argc < 0 ? 1 : argc];
+      mrb_value kdict;
+      int kargs = kd;
 
+      /* arguments is passed with Array */
       if (argc < 0) {
         struct RArray *ary = mrb_ary_ptr(regs[1]);
         argv = ARY_PTR(ary);
         argc = (int)ARY_LEN(ary);
         mrb_gc_protect(mrb, regs[1]);
       }
+
+      /* strict argument check */
       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;
-          }
+        if (argc < m1 + m2 || (r == 0 && argc > len + kd)) {
+          argnum_error(mrb, m1+m2);
+          goto L_RAISE;
         }
       }
+      /* extract first argument array to arguments */
       else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
         mrb_gc_protect(mrb, argv[0]);
         argc = (int)RARRAY_LEN(argv[0]);
         argv = RARRAY_PTR(argv[0]);
       }
-      if (argc < len) {
+
+      if (kd) {
+        /* check last arguments is hash if method takes keyword arguments */
+        if (argc == m1+m2) {
+          kdict = mrb_hash_new(mrb);
+          kargs = 0;
+        }
+        else {
+          if (argv && argc > 0 && mrb_hash_p(argv[argc-1])) {
+            kdict = argv[argc-1];
+            mrb_hash_check_kdict(mrb, kdict);
+          }
+          else if (r || argc <= m1+m2+o
+                   || !(mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc))) {
+            kdict = mrb_hash_new(mrb);
+            kargs = 0;
+          }
+          else {
+            argnum_error(mrb, m1+m2);
+            goto L_RAISE;
+          }
+          if (MRB_ASPEC_KEY(a) > 0) {
+            kdict = mrb_hash_dup(mrb, kdict);
+          }
+        }
+      }
+
+      /* no rest arguments */
+      if (argc-kargs < len) {
         int mlen = m2;
         if (argc < m1+m2) {
-          if (m1 < argc)
-            mlen = argc - m1;
-          else
-            mlen = 0;
+          mlen = m1 < argc ? argc - m1 : 0;
         }
-        regs[len+1] = *blk; /* move block */
-        SET_NIL_VALUE(regs[argc+1]);
+        regs[blk_pos] = *blk; /* move block */
+        if (kd) regs[len + 1] = kdict;
+
+        /* copy mandatory and optional arguments */
         if (argv0 != argv) {
           value_move(&regs[1], argv, argc-mlen); /* m1 + o */
         }
         if (argc < m1) {
           stack_clear(&regs[argc+1], m1-argc);
         }
+        /* copy post mandatory arguments */
         if (mlen) {
           value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
         }
         if (mlen < m2) {
           stack_clear(&regs[len-m2+mlen+1], m2-mlen);
         }
+        /* initalize rest arguments with empty Array */
         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;
+        /* skip initailizer of passed arguments */
+        if (o > 0 && argc-kargs > m1+m2)
+          pc += (argc - kargs - m1 - m2)*3;
       }
       else {
         int rnum = 0;
         if (argv0 != argv) {
-          regs[len+1] = *blk; /* move block */
+          regs[blk_pos] = *blk; /* move block */
+          if (kd) regs[len + 1] = kdict;
           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);
+          mrb_value ary;
+
+          rnum = argc-m1-o-m2-kargs;
+          ary = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
+          regs[m1+o+1] = ary;
         }
         if (m2) {
           if (argc-m2 > m1) {
@@ -1853,36 +1876,74 @@ RETRY_TRY_BLOCK:
           }
         }
         if (argv0 == argv) {
-          regs[len+1] = *blk; /* move block */
+          regs[blk_pos] = *blk; /* move block */
+          if (kd) regs[len + 1] = kdict;
         }
-        pc += o + 1;
+        pc += o*3;
       }
-      mrb->c->ci->argc = len;
+
+      /* format arguments for generated code */
+      mrb->c->ci->argc = len + kd;
+
       /* clear local (but non-argument) variables */
-      if (irep->nlocals-len-2 > 0) {
-        stack_clear(&regs[len+2], irep->nlocals-len-2);
+      if (irep->nlocals-blk_pos-1 > 0) {
+        stack_clear(&regs[blk_pos+1], irep->nlocals-blk_pos-1);
       }
       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 */
+    CASE(OP_KARG, BB) {
+      mrb_value k = mrb_symbol_value(syms[b]);
+      mrb_value kdict = regs[mrb->c->ci->argc];
+
+      if (!mrb_hash_p(kdict) || !mrb_hash_key_p(mrb, kdict, k)) {
+        mrb_value str = mrb_format(mrb, "missing keyword: %S", k);
+        mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
+        goto L_RAISE;
+      }
+      regs[a] = mrb_hash_get(mrb, kdict, k);
+      mrb_hash_delete_key(mrb, kdict, k);
       NEXT;
     }
 
-    CASE(OP_KDICT) {
-      /* A C            R(A) := kdict */
+    CASE(OP_KEY_P, BB) {
+      mrb_value k = mrb_symbol_value(syms[b]);
+      mrb_value kdict = regs[mrb->c->ci->argc];
+      mrb_bool key_p = FALSE;
+
+      if (mrb_hash_p(kdict)) {
+        key_p = mrb_hash_key_p(mrb, kdict, k);
+      }
+      regs[a] = mrb_bool_value(key_p);
       NEXT;
     }
 
+    CASE(OP_KEYEND, Z) {
+      mrb_value kdict = regs[mrb->c->ci->argc];
+
+      if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) {
+        mrb_value keys = mrb_hash_keys(mrb, kdict);
+        mrb_value key1 = RARRAY_PTR(keys)[0];
+        mrb_value str = mrb_format(mrb, "unknown keyword: %S", key1);
+        mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
+        goto L_RAISE;
+      }
+      NEXT;
+    }
+
+    CASE(OP_BREAK, B) {
+      c = OP_R_BREAK;
+      goto L_RETURN;
+    }
+    CASE(OP_RETURN_BLK, B) {
+      c = OP_R_RETURN;
+      goto L_RETURN;
+    }
+    CASE(OP_RETURN, B)
+    c = OP_R_NORMAL;
     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;
+    {
+       mrb_callinfo *ci;
 
 #define ecall_adjust() do {\
   ptrdiff_t cioff = ci - mrb->c->cibase;\
@@ -1940,7 +2001,7 @@ RETRY_TRY_BLOCK:
                 while (c->eidx > ci->epos) {
                   ecall_adjust();
                 }
-                mrb->c->status = MRB_FIBER_TERMINATED;
+                c->status = MRB_FIBER_TERMINATED;
                 mrb->c = c->prev;
                 c->prev = NULL;
                 goto L_RAISE;
@@ -1964,8 +2025,8 @@ RETRY_TRY_BLOCK:
         if (ci < ci0) {
           mrb->c->stack = ci[1].stackent;
         }
-        stack_extend(mrb, irep->nregs);
-        pc = mrb->c->rescue[--ci->ridx];
+        mrb_stack_extend(mrb, irep->nregs);
+        pc = irep->iseq+mrb->c->rescue[--ci->ridx];
       }
       else {
         int acc;
@@ -1973,9 +2034,9 @@ RETRY_TRY_BLOCK:
         struct RProc *dst;
 
         ci = mrb->c->ci;
-        v = regs[GETARG_A(i)];
+        v = regs[a];
         mrb_gc_protect(mrb, v);
-        switch (GETARG_B(i)) {
+        switch (c) {
         case OP_R_RETURN:
           /* Fall through to OP_R_NORMAL otherwise */
           if (ci->acc >=0 && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) {
@@ -2007,22 +2068,21 @@ RETRY_TRY_BLOCK:
         case OP_R_NORMAL:
         NORMAL_RETURN:
           if (ci == mrb->c->cibase) {
-            struct mrb_context *c;
+            struct mrb_context *c = mrb->c;
 
-            if (!mrb->c->prev) { /* toplevel return */
-              localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
-              goto L_RAISE;
+            if (!c->prev) { /* toplevel return */
+              regs[irep->nlocals] = v;
+              goto L_STOP;
             }
-            if (mrb->c->prev->ci == mrb->c->prev->cibase) {
+            if (c->prev->ci == 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) {
+            while (c->eidx > 0) {
               ecall(mrb);
             }
             /* automatic yield at the end */
-            c = mrb->c;
             c->status = MRB_FIBER_TERMINATED;
             mrb->c = c->prev;
             c->prev = NULL;
@@ -2032,7 +2092,7 @@ RETRY_TRY_BLOCK:
           break;
         case OP_R_BREAK:
           if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN;
-          if (MRB_PROC_ORPHAN_P(proc)) { 
+          if (MRB_PROC_ORPHAN_P(proc)) {
             mrb_value exc;
 
           L_BREAK_ERROR:
@@ -2130,91 +2190,12 @@ RETRY_TRY_BLOCK:
       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 b = GETARG_B(i);
-      int n = GETARG_C(i);
-      mrb_method_t m;
-      struct RClass *c;
-      mrb_callinfo *ci;
-      mrb_value recv;
-      mrb_sym mid = syms[b];
-
-      recv = regs[a];
-      c = mrb_class(mrb, recv);
-      m = mrb_method_search_vm(mrb, &c, mid);
-      if (MRB_METHOD_UNDEF_P(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 (MRB_METHOD_UNDEF_P(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_METHOD_CFUNC_P(m)) {
-        mrb_value v = MRB_METHOD_CFUNC(m)(mrb, recv);
-        mrb->c->stack[0] = v;
-        mrb_gc_arena_restore(mrb, ai);
-        goto L_RETURN;
-      }
-      else {
-        /* setup environment for calling method */
-        struct RProc *p = MRB_METHOD_PROC(m);
-        irep = p->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;
+    CASE(OP_BLKPUSH, BS) {
+      int m1 = (b>>11)&0x3f;
+      int r  = (b>>10)&0x1;
+      int m2 = (b>>5)&0x1f;
+      int kd = (b>>4)&0x1;
+      int lv = (b>>0)&0xf;
       mrb_value *stack;
 
       if (lv == 0) stack = regs + 1;
@@ -2231,7 +2212,7 @@ RETRY_TRY_BLOCK:
         localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
         goto L_RAISE;
       }
-      regs[a] = stack[m1+r+m2];
+      regs[a] = stack[m1+r+m2+kd];
       NEXT;
     }
 
@@ -2240,10 +2221,7 @@ RETRY_TRY_BLOCK:
   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);
-
+    CASE(OP_ADD, B) {
       /* 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):
@@ -2297,16 +2275,15 @@ RETRY_TRY_BLOCK:
         regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]);
         break;
       default:
-        goto L_SEND;
+        c = 1;
+        mid = mrb_intern_lit(mrb, "+");
+        goto L_SEND_SYM;
       }
       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);
-
+    CASE(OP_SUB, B) {
       /* 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):
@@ -2356,15 +2333,14 @@ RETRY_TRY_BLOCK:
         break;
 #endif
       default:
-        goto L_SEND;
+        c = 1;
+        mid = mrb_intern_lit(mrb, "-");
+        goto L_SEND_SYM;
       }
       NEXT;
     }
 
-    CASE(OP_MUL) {
-      /* A B C  R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)*/
-      int a = GETARG_A(i);
-
+    CASE(OP_MUL, B) {
       /* 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):
@@ -2414,14 +2390,14 @@ RETRY_TRY_BLOCK:
         break;
 #endif
       default:
-        goto L_SEND;
+        c = 1;
+        mid = mrb_intern_lit(mrb, "*");
+        goto L_SEND_SYM;
       }
       NEXT;
     }
 
-    CASE(OP_DIV) {
-      /* A B C  R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/
-      int a = GETARG_A(i);
+    CASE(OP_DIV, B) {
 #ifndef MRB_WITHOUT_FLOAT
       double x, y, f;
 #endif
@@ -2454,7 +2430,9 @@ RETRY_TRY_BLOCK:
         break;
 #endif
       default:
-        goto L_SEND;
+        c = 1;
+        mid = mrb_intern_lit(mrb, "/");
+        goto L_SEND_SYM;
       }
 
 #ifndef MRB_WITHOUT_FLOAT
@@ -2471,16 +2449,13 @@ RETRY_TRY_BLOCK:
       NEXT;
     }
 
-    CASE(OP_ADDI) {
-      /* A B C  R(A) := R(A)+C (Syms[B]=:+)*/
-      int a = GETARG_A(i);
-
+    CASE(OP_ADDI, BB) {
       /* 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 y = (mrb_int)b;
           mrb_int z;
 
           if (mrb_int_add_overflow(x, y, &z)) {
@@ -2497,24 +2472,23 @@ RETRY_TRY_BLOCK:
 #ifdef MRB_WORD_BOXING
         {
           mrb_float x = mrb_float(regs[a]);
-          SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i));
+          SET_FLOAT_VALUE(mrb, regs[a], x + b);
         }
 #else
-        mrb_float(regs[a]) += GETARG_C(i);
+        mrb_float(regs[a]) += b;
 #endif
         break;
 #endif
       default:
-        SET_INT_VALUE(regs[a+1], GETARG_C(i));
-        i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
-        goto L_SEND;
+        SET_INT_VALUE(regs[a+1], b);
+        c = 1;
+        mid = mrb_intern_lit(mrb, "+");
+        goto L_SEND_SYM;
       }
       NEXT;
     }
 
-    CASE(OP_SUBI) {
-      /* A B C  R(A) := R(A)-C (Syms[B]=:-)*/
-      int a = GETARG_A(i);
+    CASE(OP_SUBI, BB) {
       mrb_value *regs_a = regs + a;
 
       /* need to check if + is overridden */
@@ -2522,7 +2496,7 @@ RETRY_TRY_BLOCK:
       case MRB_TT_FIXNUM:
         {
           mrb_int x = mrb_fixnum(regs_a[0]);
-          mrb_int y = GETARG_C(i);
+          mrb_int y = (mrb_int)b;
           mrb_int z;
 
           if (mrb_int_sub_overflow(x, y, &z)) {
@@ -2539,17 +2513,18 @@ RETRY_TRY_BLOCK:
 #ifdef MRB_WORD_BOXING
         {
           mrb_float x = mrb_float(regs[a]);
-          SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i));
+          SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)b);
         }
 #else
-        mrb_float(regs_a[0]) -= GETARG_C(i);
+        mrb_float(regs_a[0]) -= b;
 #endif
         break;
 #endif
       default:
-        SET_INT_VALUE(regs_a[1], GETARG_C(i));
-        i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
-        goto L_SEND;
+        SET_INT_VALUE(regs_a[1], b);
+        c = 1;
+        mid = mrb_intern_lit(mrb, "-");
+        goto L_SEND_SYM;
       }
       NEXT;
     }
@@ -2565,7 +2540,9 @@ RETRY_TRY_BLOCK:
     result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
     break;\
   default:\
-    goto L_SEND;\
+    c = 1;\
+    mid = mrb_intern_lit(mrb, # op);\
+    goto L_SEND_SYM;\
   }\
   if (result) {\
     SET_TRUE_VALUE(regs[a]);\
@@ -2592,7 +2569,9 @@ RETRY_TRY_BLOCK:
     result = OP_CMP_BODY(op,mrb_float,mrb_float);\
     break;\
   default:\
-    goto L_SEND;\
+    c = 1;\
+    mid = mrb_intern_lit(mrb, # op);\
+    goto L_SEND_SYM;\
   }\
   if (result) {\
     SET_TRUE_VALUE(regs[a]);\
@@ -2603,9 +2582,7 @@ RETRY_TRY_BLOCK:
 } while(0)
 #endif
 
-    CASE(OP_EQ) {
-      /* A B C  R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/
-      int a = GETARG_A(i);
+    CASE(OP_EQ, B) {
       if (mrb_obj_eq(mrb, regs[a], regs[a+1])) {
         SET_TRUE_VALUE(regs[a]);
       }
@@ -2615,68 +2592,64 @@ RETRY_TRY_BLOCK:
       NEXT;
     }
 
-    CASE(OP_LT) {
-      /* A B C  R(A) := R(A)<R(A+1) (Syms[B]=:<,C=1)*/
-      int a = GETARG_A(i);
+    CASE(OP_LT, B) {
       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);
+    CASE(OP_LE, B) {
       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);
+    CASE(OP_GT, B) {
       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);
+    CASE(OP_GE, B) {
       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);
+    CASE(OP_ARRAY, BB) {
+      mrb_value v = mrb_ary_new_from_values(mrb, b, &regs[a]);
+      regs[a] = v;
+      mrb_gc_arena_restore(mrb, ai);
+      NEXT;
+    }
+    CASE(OP_ARRAY2, BBB) {
       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]);
+    CASE(OP_ARYCAT, B) {
+      mrb_value splat = mrb_ary_splat(mrb, regs[a+1]);
       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]);
+    CASE(OP_ARYPUSH, B) {
+      mrb_ary_push(mrb, regs[a], regs[a+1]);
       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);
+    CASE(OP_ARYDUP, B) {
+      mrb_value ary = regs[a];
+      if (mrb_array_p(ary)) {
+        ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary));
+      }
+      else {
+        ary = mrb_ary_new_from_values(mrb, 1, &ary);
+      }
+      regs[a] = ary;
+      NEXT;
+    }
+
+    CASE(OP_AREF, BBB) {
       mrb_value v = regs[b];
 
       if (!mrb_array_p(v)) {
@@ -2694,21 +2667,15 @@ RETRY_TRY_BLOCK:
       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);
+    CASE(OP_ASET, BBB) {
       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);
+    CASE(OP_APOST, BBB) {
       mrb_value v = regs[a];
-      int pre  = GETARG_B(i);
-      int post = GETARG_C(i);
+      int pre  = b;
+      int post = c;
       struct RArray *ary;
       int len, idx;
 
@@ -2739,48 +2706,65 @@ RETRY_TRY_BLOCK:
       NEXT;
     }
 
-    CASE(OP_STRING) {
-      /* A Bx           R(A) := str_new(Lit(Bx)) */
-      mrb_int a = GETARG_A(i);
-      mrb_int bx = GETARG_Bx(i);
-      mrb_value str = mrb_str_dup(mrb, pool[bx]);
+    CASE(OP_INTERN, B) {
+      mrb_sym sym = mrb_intern_str(mrb, regs[a]);
+
+      regs[a] = mrb_symbol_value(sym);
+      mrb_gc_arena_restore(mrb, ai);
+      NEXT;
+    }
+
+    CASE(OP_STRING, BB) {
+      mrb_value str = mrb_str_dup(mrb, pool[b]);
 
       regs[a] = str;
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
-    CASE(OP_STRCAT) {
-      /* A B    R(A).concat(R(B)) */
-      mrb_int a = GETARG_A(i);
-      mrb_int b = GETARG_B(i);
+    CASE(OP_STRCAT, B) {
+      mrb_str_concat(mrb, regs[a], regs[a+1]);
+      NEXT;
+    }
+
+    CASE(OP_HASH, BB) {
+      mrb_value hash = mrb_hash_new_capa(mrb, b);
+      int i;
+      int lim = a+b*2;
 
-      mrb_str_concat(mrb, regs[a], regs[b]);
+      for (i=a; i<lim; i+=2) {
+        mrb_hash_set(mrb, hash, regs[i], regs[i+1]);
+      }
+      regs[a] = hash;
+      mrb_gc_arena_restore(mrb, ai);
       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);
+    CASE(OP_HASHADD, BB) {
+      mrb_value hash;
+      int i;
+      int lim = a+b*2+1;
 
-      while (b < lim) {
-        mrb_hash_set(mrb, hash, regs[b], regs[b+1]);
-        b+=2;
+      hash = mrb_ensure_hash_type(mrb, regs[a]);
+      for (i=a+1; i<lim; i+=2) {
+        mrb_hash_set(mrb, hash, regs[i], regs[i+1]);
       }
-      regs[GETARG_A(i)] = hash;
+      mrb_gc_arena_restore(mrb, ai);
+      NEXT;
+    }
+    CASE(OP_HASHCAT, B) {
+      mrb_value hash = mrb_ensure_hash_type(mrb, regs[a]);
+
+      mrb_hash_merge(mrb, hash, regs[a+1]);
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
-    CASE(OP_LAMBDA) {
-      /* A b c  R(A) := lambda(SEQ[b],c) (b:c = 14:2) */
+    CASE(OP_LAMBDA, BB)
+    c = OP_L_LAMBDA;
+    L_MAKE_LAMBDA:
+    {
       struct RProc *p;
-      int a = GETARG_A(i);
-      int b = GETARG_b(i);
-      int c = GETARG_c(i);
       mrb_irep *nirep = irep->reps[b];
 
       if (c & OP_L_CAPTURE) {
@@ -2795,19 +2779,38 @@ RETRY_TRY_BLOCK:
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
+    CASE(OP_BLOCK, BB) {
+      c = OP_L_BLOCK;
+      goto L_MAKE_LAMBDA;
+    }
+    CASE(OP_METHOD, BB) {
+      c = OP_L_METHOD;
+      goto L_MAKE_LAMBDA;
+    }
 
-    CASE(OP_OCLASS) {
-      /* A      R(A) := ::Object */
-      regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class);
+    CASE(OP_RANGE_INC, B) {
+      mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], FALSE);
+      regs[a] = val;
+      mrb_gc_arena_restore(mrb, ai);
+      NEXT;
+    }
+
+    CASE(OP_RANGE_EXC, B) {
+      mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], TRUE);
+      regs[a] = val;
+      mrb_gc_arena_restore(mrb, ai);
+      NEXT;
+    }
+
+    CASE(OP_OCLASS, B) {
+      regs[a] = mrb_obj_value(mrb->object_class);
       NEXT;
     }
 
-    CASE(OP_CLASS) {
-      /* A B    R(A) := newclass(R(A),Syms(B),R(A+1)) */
+    CASE(OP_CLASS, BB) {
       struct RClass *c = 0, *baseclass;
-      int a = GETARG_A(i);
       mrb_value base, super;
-      mrb_sym id = syms[GETARG_B(i)];
+      mrb_sym id = syms[b];
 
       base = regs[a];
       super = regs[a+1];
@@ -2821,32 +2824,27 @@ RETRY_TRY_BLOCK:
       NEXT;
     }
 
-    CASE(OP_MODULE) {
-      /* A B            R(A) := newmodule(R(A),Syms(B)) */
-      struct RClass *c = 0, *baseclass;
-      int a = GETARG_A(i);
+    CASE(OP_MODULE, BB) {
+      struct RClass *cls = 0, *baseclass;
       mrb_value base;
-      mrb_sym id = syms[GETARG_B(i)];
+      mrb_sym id = syms[b];
 
       base = regs[a];
       if (mrb_nil_p(base)) {
         baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
         base = mrb_obj_value(baseclass);
       }
-      c = mrb_vm_define_module(mrb, base, id);
-      regs[a] = mrb_obj_value(c);
+      cls = mrb_vm_define_module(mrb, base, id);
+      regs[a] = mrb_obj_value(cls);
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
-    CASE(OP_EXEC) {
-      /* A Bx   R(A) := blockexec(R(A),SEQ[Bx]) */
-      int a = GETARG_A(i);
-      int bx = GETARG_Bx(i);
+    CASE(OP_EXEC, BB) {
       mrb_callinfo *ci;
       mrb_value recv = regs[a];
       struct RProc *p;
-      mrb_irep *nirep = irep->reps[bx];
+      mrb_irep *nirep = irep->reps[b];
 
       /* prepare closure */
       p = mrb_proc_new(mrb, nirep);
@@ -2857,7 +2855,7 @@ RETRY_TRY_BLOCK:
 
       /* prepare call stack */
       ci = cipush(mrb);
-      ci->pc = pc + 1;
+      ci->pc = pc;
       ci->acc = a;
       ci->mid = 0;
       ci->stackent = mrb->c->stack;
@@ -2873,63 +2871,59 @@ RETRY_TRY_BLOCK:
       irep = p->body.irep;
       pool = irep->pool;
       syms = irep->syms;
-      ci->nregs = irep->nregs;
-      stack_extend(mrb, ci->nregs);
-      stack_clear(regs+1, ci->nregs-1);
+      mrb_stack_extend(mrb, irep->nregs);
+      stack_clear(regs+1, irep->nregs-1);
       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]);
+    CASE(OP_DEF, BB) {
+      struct RClass *target = mrb_class_ptr(regs[a]);
       struct RProc *p = mrb_proc_ptr(regs[a+1]);
       mrb_method_t m;
 
       MRB_METHOD_FROM_PROC(m, p);
-      mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], m);
+      mrb_define_method_raw(mrb, target, syms[b], m);
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
-    CASE(OP_SCLASS) {
-      /* A B    R(A) := R(B).singleton_class */
-      int a = GETARG_A(i);
-      int b = GETARG_B(i);
-
-      regs[a] = mrb_singleton_class(mrb, regs[b]);
+    CASE(OP_SCLASS, B) {
+      regs[a] = mrb_singleton_class(mrb, regs[a]);
       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);
+    CASE(OP_TCLASS, B) {
+      if (!check_target_class(mrb)) goto L_RAISE;
+      regs[a] = 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);
+    CASE(OP_ALIAS, BB) {
+      struct RClass *target;
+
+      if (!check_target_class(mrb)) goto L_RAISE;
+      target = mrb->c->ci->target_class;
+      mrb_alias_method(mrb, target, syms[a], syms[b]);
+      NEXT;
+    }
+    CASE(OP_UNDEF, B) {
+      struct RClass *target;
+
+      if (!check_target_class(mrb)) goto L_RAISE;
+      target = mrb->c->ci->target_class;
+      mrb_undef_method_id(mrb, target, syms[a]);
       NEXT;
     }
 
-    CASE(OP_DEBUG) {
-      /* A B C    debug print R(A),R(B),R(C) */
+    CASE(OP_DEBUG, Z) {
+      FETCH_BBB();
 #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));
+      printf("OP_DEBUG %d %d %d\n", a, b, c);
 #else
       abort();
 #endif
@@ -2937,12 +2931,54 @@ RETRY_TRY_BLOCK:
       NEXT;
     }
 
-    CASE(OP_STOP) {
+    CASE(OP_ERR, B) {
+      mrb_value msg = mrb_str_dup(mrb, pool[a]);
+      mrb_value exc;
+
+      exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
+      ERR_PC_SET(mrb);
+      mrb_exc_set(mrb, exc);
+      goto L_RAISE;
+    }
+
+    CASE(OP_EXT1, Z) {
+      insn = READ_B();
+      switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+      }
+      pc--;
+      NEXT;
+    }
+    CASE(OP_EXT2, Z) {
+      insn = READ_B();
+      switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+      }
+      pc--;
+      NEXT;
+    }
+    CASE(OP_EXT3, Z) {
+      uint8_t insn = READ_B();
+      switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+      }
+      pc--;
+      NEXT;
+    }
+
+    CASE(OP_STOP, Z) {
       /*        stop VM */
     L_STOP:
       while (mrb->c->eidx > 0) {
         ecall(mrb);
       }
+      mrb->c->cibase->ridx = 0;
       ERR_PC_CLR(mrb);
       mrb->jmp = prev_jmp;
       if (mrb->exc) {
@@ -2950,26 +2986,9 @@ RETRY_TRY_BLOCK:
       }
       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);
-      }
-      ERR_PC_SET(mrb, pc);
-      mrb_exc_set(mrb, exc);
-      goto L_RAISE;
-    }
   }
   END_DISPATCH;
 #undef regs
-
   }
   MRB_CATCH(&c_jmp) {
     exc_catched = TRUE;
@@ -3003,7 +3022,6 @@ mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int sta
   }
   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);
index 540aa3e..ab5a15b 100644 (file)
@@ -1,9 +1,10 @@
 MRuby.each_target do
-  file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t|
+  file libmruby_static => libmruby_objs.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|
+  file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libmruby_static] do |t|
+    FileUtils.mkdir_p File.dirname t.name
     open(t.name, 'w') do |f|
       f.puts "MRUBY_CFLAGS = #{cc.all_flags}"
 
@@ -17,7 +18,7 @@ MRuby.each_target do
       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")}"
+      f.puts "MRUBY_LIBMRUBY_PATH = #{libmruby_static}"
     end
   end
   task :all => "#{build_dir}/lib/libmruby.flags.mak"
index 1b96452..fb76856 100644 (file)
@@ -5,7 +5,7 @@ MRuby.each_target do
     gems.check self
 
     # loader all gems
-    self.libmruby << objfile("#{build_dir}/mrbgems/gem_init")
+    self.libmruby_objs << 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"
@@ -18,7 +18,7 @@ MRuby.each_target do
         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|
+        gem_final_calls = gem_func_gems.reverse_each.with_object('') do |g, s|
           s << "  GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb);\n"
         end
         f.puts %Q[/*]
@@ -53,6 +53,7 @@ MRuby.each_target do
 
   # legal documents
   file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t|
+    FileUtils.mkdir_p File.dirname t.name
     open(t.name, 'w+') do |f|
      f.puts <<LEGAL
 Copyright (c) #{Time.now.year} mruby developers
index c59da7f..c7df9ef 100644 (file)
@@ -7,6 +7,7 @@ class MRuby::Toolchain::Android
   DEFAULT_NDK_HOMES = %w{
     /usr/local/opt/android-sdk/ndk-bundle
     /usr/local/opt/android-ndk
+    ~/Android/Sdk/ndk-bundle
     %LOCALAPPDATA%/Android/android-sdk/ndk-bundle
     %LOCALAPPDATA%/Android/android-ndk
     ~/Library/Android/sdk/ndk-bundle
index c75fa03..2832dad 100644 (file)
@@ -5,5 +5,5 @@ MRuby::Toolchain.new(:clang) do |conf, _params|
     cc.command = ENV['CC'] || 'clang'
   end
   conf.cxx.command = ENV['CXX'] || 'clang++'
-  conf.linker.command = ENV['LD'] || 'clang'
+  conf.linker.command = ENV['LD'] || ENV['CXX'] || ENV['CC'] || 'clang'
 end
index fc2e0bf..663fef9 100644 (file)
@@ -1,8 +1,7 @@
 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.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings -Wundef)]
     cc.option_include_path = '-I%s'
     cc.option_define = '-D%s'
     cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
@@ -12,8 +11,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
 
   [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.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration -Wundef)]
     cxx.option_include_path = '-I%s'
     cxx.option_define = '-D%s'
     cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
@@ -22,7 +20,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
   end
 
   conf.linker do |linker|
-    linker.command = ENV['LD'] || 'gcc'
+    linker.command = ENV['LD'] || ENV['CXX'] || ENV['CC'] || 'gcc'
     linker.flags = [ENV['LDFLAGS'] || %w()]
     linker.libraries = %w(m)
     linker.library_paths = []
@@ -55,7 +53,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
       @header_search_paths
     end
   end
-  
+
   def conf.enable_sanitizer(*opts)
     fail 'sanitizer already set' if @sanitizer_list
 
index 1637f6d..aeb6dbc 100644 (file)
@@ -5,7 +5,6 @@ MRuby::Toolchain.new(:openwrt) do |conf|
     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}'
@@ -15,7 +14,6 @@ MRuby::Toolchain.new(:openwrt) do |conf|
     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}'
index b008273..6275059 100644 (file)
@@ -3,7 +3,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
     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.defines = %w(MRB_STACK_EXTEND_DOUBLING)
     cc.option_include_path = '/I%s'
     cc.option_define = '/D%s'
     cc.compile_options = "%{flags} /Fo%{outfile} %{infile}"
@@ -14,7 +14,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
   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.defines = %w(MRB_STACK_EXTEND_DOUBLING)
     cxx.option_include_path = '/I%s'
     cxx.option_define = '/D%s'
     cxx.compile_options = "%{flags} /Fo%{outfile} %{infile}"
index a9baae5..c57b04c 100644 (file)
@@ -1,17 +1,16 @@
 $ok_test = 0
 $ko_test = 0
 $kill_test = 0
+$skip_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
+unless RUBY_ENGINE == "mruby"
+  # For bintest on Ruby
+  def t_print(*args)
+    print(*args)
+    $stdout.flush
+    nil
   end
 end
 
@@ -19,13 +18,14 @@ 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.cause}" if e && e.respond_to?(:cause)
-  msg += " => #{e.message}" if e && !e.respond_to?(:cause)
-  msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME)
+  msg += " [#{iso}]" if iso && !iso.empty?
+  msg += " => #{e}" if e && !e.to_s.empty?
+  msg += " (#{GEMNAME == 'mruby-test' ? 'core' : "mrbgems: #{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}"
+      msg += "\n - Assertion[#{idx}]"
+      msg += " #{assert_msg}." if assert_msg && !assert_msg.empty?
+      msg += "\n#{diff}" if diff && !diff.empty?
     end
   end
   msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt
@@ -47,23 +47,22 @@ def assert(str = 'Assertion failed', iso = '')
     $mrbtest_assert_idx = 0
     yield
     if($mrbtest_assert.size > 0)
-      $asserts.push(assertion_string('Fail: ', str, iso, nil))
+      $asserts.push(assertion_string('Fail: ', str, iso))
       $ko_test += 1
       t_print('F')
     else
       $ok_test += 1
       t_print('.')
     end
+  rescue MRubyTestSkip => e
+    $asserts.push(assertion_string('Skip: ', str, iso, e))
+    $skip_test += 1
+    t_print('?')
   rescue Exception => e
     bt = e.backtrace if $mrbtest_verbose
-    if e.class.to_s == 'MRubyTestSkip'
-      $asserts.push(assertion_string('Skip: ', str, iso, e, nil))
-      t_print('?')
-    else
-      $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
-      $kill_test += 1
-      t_print('X')
-    end
+    $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
+    $kill_test += 1
+    t_print('X')
   ensure
     $mrbtest_assert = nil
   end
@@ -71,178 +70,172 @@ def assert(str = 'Assertion failed', iso = '')
 end
 
 def assertion_diff(exp, act)
-  "    Expected: #{exp.inspect}\n" +
+  "    Expected: #{exp.inspect}\n" \
   "      Actual: #{act.inspect}"
 end
 
-def assert_true(ret, msg = nil, diff = nil)
+def assert_true(obj, 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
+    unless obj == true
+      diff ||= "    Expected #{obj.inspect} to be true."
       $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
     end
   end
-  ret
+  obj
 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
+def assert_false(obj, msg = nil, diff = nil)
+  unless obj == false
+    diff ||= "    Expected #{obj.inspect} to be false."
   end
-  !ret
+  assert_true(!obj, msg, diff)
 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
+def assert_equal(exp, act_or_msg = nil, msg = nil, &block)
+  ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block)
+  unless ret
+    diff = assertion_diff(exp, act)
   end
-
-  msg = "Expected to be equal" unless msg
-  diff = assertion_diff(exp, act)
-  assert_true(exp == act, msg, diff)
+  assert_true(ret, 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
+def assert_not_equal(exp, act_or_msg = nil, msg = nil, &block)
+  ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block)
+  if ret
+    diff = "    Expected #{act.inspect} to not be equal to #{exp.inspect}."
   end
+  assert_true(!ret, msg, diff)
+end
 
-  msg = "Expected to be not equal" unless msg
-  diff = assertion_diff(exp, act)
-  assert_false(exp == act, msg, diff)
+def assert_same(*args); _assert_same(true, *args) end
+def assert_not_same(*args); _assert_same(false, *args) end
+def _assert_same(affirmed, exp, act, msg = nil)
+  unless ret = exp.equal?(act) == affirmed
+    exp_str, act_str = [exp, act].map do |o|
+      "#{o.inspect} (class=#{o.class}, oid=#{o.__id__})"
+    end
+    diff = "    Expected #{act_str} to #{'not ' unless affirmed}be the same as #{exp_str}."
+  end
+  assert_true(ret, 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)
+  unless ret = obj.nil?
+    diff = "    Expected #{obj.inspect} to be nil."
+  end
+  assert_true(ret, 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)
+def assert_include(*args); _assert_include(true, *args) end
+def assert_not_include(*args); _assert_include(false, *args) end
+def _assert_include(affirmed, collection, obj, msg = nil)
+  unless ret = collection.include?(obj) == affirmed
+    diff = "    Expected #{collection.inspect} to #{'not ' unless affirmed}include #{obj.inspect}."
+  end
+  assert_true(ret, 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)
+##
+# Fails unless +obj+ is a kind of +cls+.
+def assert_kind_of(cls, obj, msg = nil)
+  unless ret = obj.kind_of?(cls)
+    diff = "    Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}."
+  end
+  assert_true(ret, msg, diff)
 end
 
-def assert_raise(*exc)
-  return true unless $mrbtest_assert
-  $mrbtest_assert_idx += 1
+##
+# Fails unless +exp+ is equal to +act+ in terms of a Float
+def assert_float(exp, act, msg = nil)
+  e, a = exp.to_f, act.to_f
+  if (e.infinite? || a.infinite?) && e != a ||
+     e.nan? && !a.nan? || !e.nan? && a.nan?
+    flunk(msg, "    Expected #{act} to be #{exp}.")
+  elsif (n = (e - a).abs) > Mrbtest::FLOAT_TOLERANCE
+    flunk(msg, "    Expected |#{exp} - #{act}| (#{n}) to be <= #{Mrbtest::FLOAT_TOLERANCE}.")
+  else
+    pass
+  end
+end
 
+def assert_raise(*exc)
   msg = (exc.last.is_a? String) ? exc.pop : nil
-
+  exc = exc.empty? ? StandardError : exc.size == 1 ? exc[0] : exc
   begin
     yield
-    msg ||= "Expected to raise #{exc} but nothing was raised."
-    diff = nil
-    $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
-    false
   rescue *exc
-    true
+    pass
   rescue Exception => e
-    msg ||= "Expected to raise #{exc}, not"
-    diff = "      Class: <#{e.class}>\n" +
-           "    Message: #{e.message}"
-    $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
-    false
+    diff = "    #{exc} exception expected, not\n" \
+           "    Class: <#{e.class}>\n" \
+           "    Message: <#{e}>"
+    flunk(msg, diff)
+  else
+    diff = "    #{exc} expected but nothing was raised."
+    flunk(msg, diff)
   end
 end
 
 def assert_nothing_raised(msg = nil)
-  return true unless $mrbtest_assert
-  $mrbtest_assert_idx += 1
-
   begin
     yield
-    true
   rescue Exception => e
-    msg ||= "Expected not to raise #{exc.join(', ')} but it raised"
-    diff =  "      Class: <#{e.class}>\n" +
-            "    Message: #{e.message}"
-    $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
-    false
+    diff = "    Exception raised:\n" \
+           "    Class: <#{e.class}>\n" \
+           "    Message: <#{e}>"
+    flunk(msg, diff)
+  else
+    pass
   end
 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)
+def pass
+  assert_true(true)
 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
+def flunk(msg = nil, diff = "Epic Fail!")
+  assert_true(false, msg, diff)
 end
 
 ##
 # Report the test result and print all assertions
 # which were reported broken.
-def report()
+def report
   t_print("\n")
 
   $asserts.each do |msg|
-    t_print "#{msg}\n"
+    t_print("#{msg}\n")
   end
 
-  $total_test = $ok_test+$ko_test+$kill_test
+  $total_test = $ok_test + $ko_test + $kill_test + $skip_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")
+  t_print(" Skip: #{$skip_test}\n")
 
   if Object.const_defined?(:Time)
     t_time = Time.now - $test_start
     t_print(" Time: #{t_time.round(2)} seconds\n")
   end
+
+  $ko_test == 0 && $kill_test == 0
 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
+def _eval_assertion(meth, exp, act_or_msg, msg, block)
+  if block
+    exp, act, msg = exp, block.call, act_or_msg
   else
-    true
+    exp, act, msg = exp, act_or_msg, msg
   end
+  return exp.__send__(meth, act), exp, act, msg
 end
 
 ##
 # Skip the test
-class MRubyTestSkip < NotImplementedError
-  attr_accessor :cause
-  def initialize(cause)
-    @cause = cause
-  end
-end
+class MRubyTestSkip < NotImplementedError; end
 
 def skip(cause = "")
   raise MRubyTestSkip.new(cause)
index 12971a9..ed71e57 100644 (file)
@@ -1,6 +1,8 @@
 $:.unshift File.dirname(File.dirname(File.expand_path(__FILE__)))
 require 'test/assert.rb'
 
+GEMNAME = ""
+
 def cmd(s)
   case RbConfig::CONFIG['host_os']
   when /mswin(?!ce)|mingw|bccwin/
@@ -19,15 +21,22 @@ def shellquote(s)
   end
 end
 
+print "bintest - Command Binary Test\n\n"
+
 ARGV.each do |gem|
+  case gem
+  when '-v'; $mrbtest_verbose = true
+  end
+
   case RbConfig::CONFIG['host_os']
   when /mswin(?!ce)|mingw|bccwin/
     gem = gem.gsub('\\', '/')
   end
 
   Dir["#{gem}/bintest/**/*.rb"].each do |file|
+    GEMNAME.replace(File.basename(gem))
     load file
   end
 end
 
-load 'test/report.rb'
+exit report
diff --git a/third-party/mruby/test/report.rb b/third-party/mruby/test/report.rb
deleted file mode 100644 (file)
index fb77fd0..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-report
-if $ko_test > 0 or $kill_test > 0
-  raise "mrbtest failed (KO:#{$ko_test}, Crash:#{$kill_test})"
-end
index d2edd01..2b19fe0 100644 (file)
@@ -55,9 +55,10 @@ assert('Array#[]', '15.2.12.5.4') do
   assert_equal(nil, [1,2,3].[](-4))
 
   a = [ "a", "b", "c", "d", "e" ]
-  assert_equal("b", a[1.1]) if class_defined?("Float")
   assert_equal(["b", "c"], a[1,2])
   assert_equal(["b", "c", "d"], a[1..-2])
+  skip unless Object.const_defined?(:Float)
+  assert_equal("b", a[1.1])
 end
 
 assert('Array#[]=', '15.2.12.5.5') do
@@ -238,7 +239,7 @@ assert('Array#pop', '15.2.12.5.21') do
   assert_equal([1,2], a)
   assert_equal(3, b)
 
-  assert_raise(RuntimeError) { [].freeze.pop }
+  assert_raise(FrozenError) { [].freeze.pop }
 end
 
 assert('Array#push', '15.2.12.5.22') do
@@ -287,7 +288,7 @@ assert('Array#shift', '15.2.12.5.27') do
   assert_equal([2,3], a)
   assert_equal(1, b)
 
-  assert_raise(RuntimeError) { [].freeze.shift }
+  assert_raise(FrozenError) { [].freeze.shift }
 end
 
 assert('Array#size', '15.2.12.5.28') do
@@ -360,13 +361,6 @@ 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)
@@ -386,9 +380,15 @@ assert("Array#rindex") do
   assert_equal 0, $a.rindex(1)
 end
 
+assert('Array#sort!') do
+  a = [3, 2, 1]
+  assert_equal a, a.sort!      # sort! returns self.
+  assert_equal [1, 2, 3], a    # it is sorted.
+end
+
 assert('Array#freeze') do
   a = [].freeze
-  assert_raise(RuntimeError) do
+  assert_raise(FrozenError) do
     a[0] = 1
   end
 end
index 62eb7e3..995e525 100644 (file)
@@ -408,42 +408,43 @@ assert('BS Block 32') do
 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
+  assert_nothing_raised 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 render(&block)
+        "in render"
+      end
     end
-    def respond
-      @response.call
+    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
+    t.test_for_bug
   end
-  t = Controller.new
-  assert_true t.test_for_bug
 end
 
 assert("BS Block 33") do
index a5118fa..290ecf7 100644 (file)
@@ -36,7 +36,7 @@ end
 
 assert('Class#new', '15.2.3.3.3') do
   assert_raise(TypeError, 'Singleton should raise TypeError') do
-    "a".singleton_class.new
+    (class <<"a"; self; end).new
   end
 
   class TestClass
@@ -236,6 +236,11 @@ assert('class to return the last value') do
   assert_equal(m, :m)
 end
 
+assert('class to return nil if body is empty') do
+  assert_nil(class C end)
+  assert_nil(class << self; end)
+end
+
 assert('raise when superclass is not a class') do
   module FirstModule; end
   assert_raise(TypeError, 'should raise TypeError') do
@@ -293,15 +298,7 @@ assert('singleton tests') do
     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_equal :run_baz, baz
 
   assert_raise(NoMethodError, 'should raise NoMethodError') do
     bar.run_foo_mod
@@ -318,8 +315,8 @@ assert('singleton tests') do
     self
   end
 
-  assert_true baz.singleton_methods.include? :run_baz
-  assert_true baz.singleton_methods.include? :run_foo_mod
+  assert_true baz.respond_to? :run_baz
+  assert_true baz.respond_to? :run_foo_mod
   assert_equal 100, baz.run_foo_mod
   assert_equal 300, baz.run_baz
 
@@ -358,7 +355,7 @@ assert('singleton tests') do
         7
       end
     end
-  end if class_defined?("Float")
+  end if Object.const_defined?(:Float)
 end
 
 assert('clone Class') do
@@ -368,7 +365,7 @@ assert('clone Class') do
     end
   end
 
-  Foo.clone.new.func
+  assert_true(Foo.clone.new.func)
 end
 
 assert('class variable and class << self style class method') do
@@ -440,12 +437,3 @@ 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 4c9e2c5..acb9e1b 100644 (file)
@@ -184,14 +184,14 @@ assert('register window of calls (#3783)') do
   # NODE_UNDEF
   assert_nothing_raised do
     class << Object.new
-      undef send
+      undef inspect
     end
   end
 
   # NODE_ALIAS
   assert_nothing_raised do
     class << Object.new
-      alias send2 send
+      alias inspect2 inspect
     end
   end
-end
\ No newline at end of file
+end
index 359c345..652c304 100644 (file)
@@ -68,7 +68,7 @@ assert('Enumerable#find', '15.3.2.2.7') do
 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]
+  assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0}
 end
 
 assert('Enumerable#grep', '15.3.2.2.9') do
index ce7b584..bdf277c 100644 (file)
@@ -263,10 +263,10 @@ assert('Exception 13') do
 end
 
 assert('Exception 14') do
-  def exception_test14; UnknownConstant; end
+  def (o = Object.new).exception_test14; UnknownConstant end
   a = :ng
   begin
-    send(:exception_test14)
+    o.__send__(:exception_test14)
   rescue
     a = :ok
   end
index 92f7a15..4e9d347 100644 (file)
@@ -1,7 +1,7 @@
 ##
 # Float ISO Test
 
-if class_defined?("Float")
+if Object.const_defined?(:Float)
 
 assert('Float', '15.2.9') do
   assert_equal Class, Float.class
@@ -206,4 +206,43 @@ assert('Float#>>') do
   assert_equal(-1, -23.0 >> 128)
 end
 
-end # class_defined?("Float")
+assert('Float#to_s') do
+  uses_float = 4e38.infinite?  # enable MRB_USE_FLOAT?
+
+  assert_equal("Infinity", Float::INFINITY.to_s)
+  assert_equal("-Infinity", (-Float::INFINITY).to_s)
+  assert_equal("NaN", Float::NAN.to_s)
+  assert_equal("0.0", 0.0.to_s)
+  assert_equal("-0.0", -0.0.to_s)
+  assert_equal("-3.25", -3.25.to_s)
+  assert_equal("50.0", 50.0.to_s)
+  assert_equal("0.0125", 0.0125.to_s)
+  assert_equal("-0.0125", -0.0125.to_s)
+  assert_equal("1.0e-10", 0.0000000001.to_s)
+  assert_equal("-1.0e-10", -0.0000000001.to_s)
+  assert_equal("1.0e+20", 1e20.to_s)
+  assert_equal("-1.0e+20", -1e20.to_s)
+  assert_equal("1.0e+16", 10000000000000000.0.to_s)
+  assert_equal("-1.0e+16", -10000000000000000.0.to_s)
+  assert_equal("100000.0", 100000.0.to_s)
+  assert_equal("-100000.0", -100000.0.to_s)
+  if uses_float
+    assert_equal("1.0e+08", 100000000.0.to_s)
+    assert_equal("-1.0e+08", -100000000.0.to_s)
+    assert_equal("1.0e+07", 10000000.0.to_s)
+    assert_equal("-1.0e+07", -10000000.0.to_s)
+  else
+    assert_equal("1.0e+15", 1000000000000000.0.to_s)
+    assert_equal("-1.0e+15", -1000000000000000.0.to_s)
+    assert_equal("100000000000000.0", 100000000000000.0.to_s)
+    assert_equal("-100000000000000.0", -100000000000000.0.to_s)
+  end
+end
+
+assert('Float#eql?') do
+  assert_true(5.0.eql?(5.0))
+  assert_false(5.0.eql?(5))
+  assert_false(5.0.eql?("5.0"))
+end
+
+end # const_defined?(:Float)
index 5403a59..156991f 100644 (file)
@@ -8,8 +8,9 @@ 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 }) if class_defined?("Float")
   assert_false({ :a => 1 } == true)
+  skip unless Object.const_defined?(:Float)
+  assert_true({ :equal => 1 } == { :equal => 1.0 })
 end
 
 assert('Hash#[]', '15.2.13.4.2') do
@@ -82,12 +83,12 @@ assert('Hash#default_proc', '15.2.13.4.7') do
 end
 
 assert('Hash#delete', '15.2.13.4.8') do
-  a = { 'abc' => 'abc' }
-  b = { 'abc' => 'abc' }
+  a = { 'abc' => 'ABC' }
+  b = { 'abc' => 'ABC' }
   b_tmp_1 = false
   b_tmp_2 = false
 
-  a.delete('abc')
+  assert_equal 'ABC', a.delete('abc')
   b.delete('abc') do |k|
     b_tmp_1 = true
   end
@@ -369,7 +370,7 @@ end
 
 assert('Hash#freeze') do
   h = {}.freeze
-  assert_raise(RuntimeError) do
+  assert_raise(FrozenError) do
     h[:a] = 'b'
   end
 end
index cea97a1..4ab49eb 100644 (file)
@@ -7,10 +7,10 @@ end
 
 assert('Integer#+', '15.2.8.3.1') do
   a = 1+1
-  b = 1+1.0 if class_defined?("Float")
+  b = 1+1.0 if Object.const_defined?(:Float)
 
   assert_equal 2, a
-  assert_equal 2.0, b if class_defined?("Float")
+  assert_equal 2.0, b if Object.const_defined?(:Float)
 
   assert_raise(TypeError){ 0+nil }
   assert_raise(TypeError){ 1+nil }
@@ -18,40 +18,38 @@ assert('Integer#+', '15.2.8.3.1') do
   c = Mrbtest::FIXNUM_MAX + 1
   d = Mrbtest::FIXNUM_MAX.__send__(:+, 1)
 
-  if class_defined?("Float")
-    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
+  skip unless Object.const_defined?(:Float)
+  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 if class_defined?("Float")
+  b = 2-1.0 if Object.const_defined?(:Float)
 
   assert_equal 1, a
-  assert_equal 1.0, b if class_defined?("Float")
+  assert_equal 1.0, b if Object.const_defined?(:Float)
 
   c = Mrbtest::FIXNUM_MIN - 1
   d = Mrbtest::FIXNUM_MIN.__send__(:-, 1)
 
-  if class_defined?("Float")
-    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
+  skip unless Object.const_defined?(:Float)
+  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 if class_defined?("Float")
+  b = 1*1.0 if Object.const_defined?(:Float)
 
   assert_equal 1, a
-  assert_equal 1.0, b if class_defined?("Float")
+  assert_equal 1.0, b if Object.const_defined?(:Float)
 
   assert_raise(TypeError){ 0*nil }
   assert_raise(TypeError){ 1*nil }
@@ -59,13 +57,12 @@ assert('Integer#*', '15.2.8.3.3') do
   c = Mrbtest::FIXNUM_MAX * 2
   d = Mrbtest::FIXNUM_MAX.__send__(:*, 2)
 
-  if class_defined?("Float")
-    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
+  skip unless Object.const_defined?(:Float)
+  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
@@ -226,8 +223,9 @@ assert('Integer#times', '15.2.8.3.22') do
 end
 
 assert('Integer#to_f', '15.2.8.3.23') do
+  skip unless Object.const_defined?(:Float)
   assert_equal 1.0, 1.to_f
-end if class_defined?("Float")
+end
 
 assert('Integer#to_i', '15.2.8.3.24') do
   assert_equal 1, 1.to_i
@@ -259,19 +257,3 @@ assert('Integer#divmod', '15.2.8.3.30') do
   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 eaae78e..d99358c 100644 (file)
@@ -92,20 +92,6 @@ 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?
@@ -171,6 +157,10 @@ assert('Kernel#clone', '15.3.1.3.8') do
   assert_true a.respond_to?(:test)
   assert_false b.respond_to?(:test)
   assert_true c.respond_to?(:test)
+
+  a.freeze
+  d = a.clone
+  assert_true d.frozen?
 end
 
 assert('Kernel#dup', '15.3.1.3.9') do
@@ -274,30 +264,6 @@ assert('Kernel#inspect', '15.3.1.3.17') do
   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)
@@ -377,10 +343,6 @@ assert('Kernel#method_missing', '15.3.1.3.30') do
   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
@@ -404,23 +366,6 @@ end
 
 # 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
@@ -446,11 +391,10 @@ assert('Kernel#remove_instance_variable', '15.3.1.3.41') do
 
   tri = Test4RemoveInstanceVar.new
   assert_equal 99, tri.var
-  tri.remove
+  assert_equal 99, tri.remove
   assert_equal nil, tri.var
-  assert_raise NameError do
-    tri.remove
-  end
+  assert_raise(NameError) { tri.remove }
+  assert_raise(NameError) { tri.remove_instance_variable(:var) }
 end
 
 # Kernel#require is defined in mruby-require. '15.3.1.3.42'
@@ -481,57 +425,10 @@ assert('Kernel#respond_to?', '15.3.1.3.43') do
   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: 15.3.1.3.28
-    local_variables.sort
-  }.call(-1, -2)
-end
-
 assert('Kernel#!=') do
   str1 = "hello"
   str2 = str1
@@ -581,15 +478,6 @@ assert('Kernel#global_variables') do
   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
index 51a37c3..6344219 100644 (file)
@@ -22,7 +22,7 @@ assert('Literals Numerical', '8.7.6.2') do
   # decimal
   assert_equal 999, 0d999
   assert_equal 999, 0D999
-  # decimal seperator
+  # decimal separator
   assert_equal 10000000, 10_000_000
   assert_equal       10, 1_0
   # integer with exponent
index 5a46c24..ec36855 100644 (file)
@@ -3,7 +3,7 @@
 
 def labeled_module(name, &block)
   Module.new do
-    singleton_class.class_eval do
+    (class <<self; self end).class_eval do
       define_method(:to_s) { name }
       alias_method :inspect, :to_s
     end
@@ -13,7 +13,7 @@ end
 
 def labeled_class(name, supklass = Object, &block)
   Class.new(supklass) do
-    singleton_class.class_eval do
+    (class <<self; self end).class_eval do
       define_method(:to_s) { name }
       alias_method :inspect, :to_s
     end
@@ -27,24 +27,9 @@ 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.nesting', '15.2.2.2.2') do
-  module Test4ModuleNesting
-    module Test4ModuleNesting2
-      assert_equal [Test4ModuleNesting2, Test4ModuleNesting],
-                   Module.nesting
-    end
-  end
-  module Test4ModuleNesting::Test4ModuleNesting2
-    assert_equal [Test4ModuleNesting::Test4ModuleNesting2], Module.nesting
-  end
-end
-
 assert('Module#ancestors', '15.2.2.4.9') do
   class Test4ModuleAncestors
   end
-  sc = Test4ModuleAncestors.singleton_class
   r = String.ancestors
 
   assert_equal Array, r.class
@@ -213,56 +198,9 @@ assert('Module#class_eval', '15.2.2.4.15') 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
+  assert_equal true, Test4ClassEval.new.respond_to?(:method1)
 end
 
 assert('Module#const_defined?', '15.2.2.4.20') do
@@ -272,6 +210,7 @@ assert('Module#const_defined?', '15.2.2.4.20') do
 
   assert_true Test4ConstDefined.const_defined?(:Const4Test4ConstDefined)
   assert_false Test4ConstDefined.const_defined?(:NotExisting)
+  assert_raise(NameError){ Test4ConstDefined.const_defined?(:wrong_name) }
 end
 
 assert('Module#const_get', '15.2.2.4.21') do
@@ -286,16 +225,7 @@ assert('Module#const_get', '15.2.2.4.21') do
   assert_raise(TypeError){ Test4ConstGet.const_get(123) }
   assert_raise(NameError){ Test4ConstGet.const_get(:I_DO_NOT_EXIST) }
   assert_raise(NameError){ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") }
-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)
+  assert_raise(NameError){ Test4ConstGet.const_get(:wrong_name) }
 end
 
 assert('Module#const_set', '15.2.2.4.23') do
@@ -303,23 +233,34 @@ assert('Module#const_set', '15.2.2.4.23') do
     Const4Test4ConstSet = 42
   end
 
-  assert_true Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
+  assert_equal 23, Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
   assert_equal 23, Test4ConstSet.const_get(:Const4Test4ConstSet)
+  ["", "wrongNAME", "Wrong-Name"].each do |n|
+    assert_raise(NameError) { Test4ConstSet.const_set(n, 1) }
+  end
 end
 
-assert('Module#constants', '15.2.2.4.24') do
-  $n = []
-  module TestA
-    C = 1
+assert('Module#remove_const', '15.2.2.4.40') do
+  module Test4RemoveConst
+    ExistingConst = 23
   end
-  class TestB
-    include TestA
-    C2 = 1
-    $n = constants.sort
+
+  assert_equal 23, Test4RemoveConst.remove_const(:ExistingConst)
+  assert_false Test4RemoveConst.const_defined?(:ExistingConst)
+  assert_raise(NameError) { Test4RemoveConst.remove_const(:NonExistingConst) }
+  %i[x X!].each do |n|
+    assert_raise(NameError) { Test4RemoveConst.remove_const(n) }
   end
+end
 
-  assert_equal [ :C ], TestA.constants
-  assert_equal [ :C, :C2 ], $n
+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#include', '15.2.2.4.27') do
@@ -366,48 +307,16 @@ assert('Module#included', '15.2.2.4.29') do
   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
+  cls = Class.new{include mod}
+  assert_true cls.new.respond_to?(:hello)
   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
@@ -431,7 +340,6 @@ assert('Module#method_defined?', '15.2.2.4.34') do
   assert_false Test4MethodDefined::C.method_defined? "method4"
 end
 
-
 assert('Module#module_eval', '15.2.2.4.35') do
   module Test4ModuleEval
     @a = 11
@@ -442,55 +350,6 @@ assert('Module#module_eval', '15.2.2.4.35') do
   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
@@ -511,7 +370,6 @@ assert('Module#undef_method', '15.2.2.4.42') do
   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
@@ -608,41 +466,6 @@ end
     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('[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")
@@ -683,12 +506,6 @@ end
     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
@@ -699,7 +516,7 @@ end
     end
   end
 
-  # these assertions will not run without a #assert_seperately method
+  # these assertions will not run without a #assert_separately method
   #assert 'test_prepend_optmethod' do
   #  bug7983 = '[ruby-dev:47124] [Bug #7983]'
   #  assert_separately [], %{
@@ -715,76 +532,61 @@ end
   #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(bug8005) {a.send :foo}
-  end
+  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(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 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 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")
@@ -807,7 +609,7 @@ end
     assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
   end
 
-  # requires #assert_seperately
+  # requires #assert_separately
   #assert 'Module#prepend call super' do
   #  assert_separately([], <<-'end;') #do
   #    bug10847 = '[ruby-core:68093] [Bug #10847]'
@@ -902,6 +704,15 @@ assert('module with non-class/module outer raises TypeError') do
   assert_raise(TypeError) { module []::M2 end }
 end
 
+assert('module to return the last value') do
+  m = module M; :m end
+  assert_equal(m, :m)
+end
+
+assert('module to return nil if body is empty') do
+  assert_nil(module M end)
+end
+
 assert('get constant of parent module in singleton class; issue #3568') do
   actual = module GetConstantInSingletonTest
     EXPECTED = "value"
index 38c62a6..d73dfdb 100644 (file)
@@ -2,7 +2,7 @@
 # Numeric ISO Test
 
 assert('Numeric', '15.2.7') do
-  assert_equal Class, Numeric.class
+  assert_equal(Class, Numeric.class)
 end
 
 assert('Numeric#+@', '15.2.7.4.1') do
@@ -15,15 +15,8 @@ end
 
 assert('Numeric#abs', '15.2.7.4.3') do
   assert_equal(1, 1.abs)
-  assert_equal(1.0, -1.abs) if class_defined?("Float")
-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)
+  skip unless Object.const_defined?(:Float)
+  assert_equal(1.0, -1.abs)
 end
 
 assert('Numeric#/', '15.2.8.3.4') do
@@ -39,5 +32,41 @@ end
 # Not ISO specified
 
 assert('Numeric#**') do
-  assert_equal 8.0, 2.0**3
+  assert_equal(8, 2 ** 3)
+  assert_equal(-8, -2 ** 3)
+  assert_equal(1, 2 ** 0)
+  skip unless Object.const_defined?(:Float)
+  assert_equal(1.0, 2.2 ** 0)
+  assert_equal(0.5, 2 ** -1)
+  assert_equal(8.0, 2.0**3)
+end
+
+assert('Numeric#step') do
+  assert_step = ->(exp, receiver, args) do
+    inf = !args[0]
+    act = []
+    ret = receiver.step(*args) do |i|
+      act << i
+      break if inf && exp.size == act.size
+    end
+    expr = "#{receiver.inspect}.step(#{args.map(&:inspect).join(', ')})"
+    assert_true(exp.eql?(act), "#{expr}: counters", assertion_diff(exp, act))
+    assert_same(receiver, ret, "#{expr}: return value") unless inf
+  end
+
+  assert_raise(ArgumentError) { 1.step(2, 0) { break } }
+  assert_step.([2, 3, 4], 2, [4])
+  assert_step.([10, 8, 6, 4, 2], 10, [1, -2])
+  assert_step.([], 2, [1, 3])
+  assert_step.([], -2, [-1, -3])
+  assert_step.([10, 11, 12, 13], 10, [])
+  assert_step.([10, 7, 4], 10, [nil, -3])
+
+  skip unless Object.const_defined?(:Float)
+  assert_raise(ArgumentError) { 1.step(2, 0.0) { break } }
+  assert_step.([2.0, 3.0, 4.0], 2, [4.0])
+  assert_step.([7.0, 4.0, 1.0, -2.0], 7, [-4, -3.0])
+  assert_step.([2.0, 3.0, 4.0], 2.0, [4])
+  assert_step.([10.0, 11.0, 12.0, 13.0], 10.0, [])
+  assert_step.([10.0, 7.0, 4.0], 10, [nil, -3.0])
 end
index 42ac3b9..b17b21e 100644 (file)
@@ -157,7 +157,7 @@ assert('&obj call to_proc if defined') do
   def mock(&b)
     b
   end
-  assert_equal pr.object_id, mock(&pr).object_id
+  assert_same pr, mock(&pr)
   assert_equal pr, mock(&pr)
 
   obj = Object.new
index 3e67fcc..d71fe89 100644 (file)
@@ -8,7 +8,8 @@ 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) if class_defined?("Float")
+  skip unless Object.const_defined?(:Float)
+  assert_true (1..10) == Range.new(1.0, 10.0)
 end
 
 assert('Range#===', '15.2.14.4.2') do
@@ -59,7 +60,7 @@ assert('Range#initialize', '15.2.14.4.9') do
   assert_equal (1..10), b
   assert_false b.exclude_end?
 
-  assert_raise(NameError) { (0..1).send(:initialize, 1, 3) }
+  assert_raise(NameError) { (0..1).__send__(:initialize, 1, 3) }
 end
 
 assert('Range#last', '15.2.14.4.10') do
@@ -93,3 +94,19 @@ assert('Range#eql?', '15.2.14.4.14') do
   assert_false (1..10).eql? (Range.new(1.0, 10.0))
   assert_false (1..10).eql? "1..10"
 end
+
+assert('Range#initialize_copy', '15.2.14.4.15') do
+  assert_raise(NameError) { (0..1).__send__(:initialize_copy, 1..3) }
+end
+
+assert('Range#dup') do
+  r = (1..3).dup
+  assert_equal r.begin, 1
+  assert_equal r.end, 3
+  assert_false r.exclude_end?
+
+  r = ("a"..."z").dup
+  assert_equal r.begin, "a"
+  assert_equal r.end, "z"
+  assert_true r.exclude_end?
+end
index e91b915..cf3702c 100644 (file)
@@ -155,7 +155,7 @@ assert('String#[]=') do
     d[-10] = 'X'
   end
 
-  if class_defined?("Float")
+  if Object.const_defined?(:Float)
    e = 'abc'
    e[1.1] = 'X'
    assert_equal 'aXc', e
@@ -253,19 +253,6 @@ assert('String#chomp!', '15.2.10.5.10') do
   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
@@ -477,12 +464,11 @@ assert('String#reverse', '15.2.10.5.29') do
 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 "世", "こんにちは世界"["世"]
+  a = 'こんにちは世界!'
+  a.reverse
+
+  assert_equal 'こんにちは世界!', a
+  assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse
 end if UTF8STRING
 
 assert('String#reverse!', '15.2.10.5.30') do
@@ -592,7 +578,7 @@ assert('String#sub', '15.2.10.5.36') do
   str = "abc"
   miss = str.sub("X", "Z")
   assert_equal str, miss
-  assert_not_equal str.object_id, miss.object_id
+  assert_not_same str, miss
 
   a = []
   assert_equal '.abc', "abc".sub("") { |i| a << i; "." }
@@ -631,7 +617,7 @@ assert('String#to_f', '15.2.10.5.38') do
   assert_float(12345.6789, c)
   assert_float(0, d)
   assert_float(Float::INFINITY, e)
-end if class_defined?("Float")
+end if Object.const_defined?(:Float)
 
 assert('String#to_i', '15.2.10.5.39') do
   a = ''.to_i
@@ -725,5 +711,11 @@ assert('String#freeze') do
   str = "hello"
   str.freeze
 
-  assert_raise(RuntimeError) { str.upcase! }
+  assert_raise(FrozenError) { str.upcase! }
+end
+
+assert('String literal concatenation') do
+  assert_equal 2, ("A" "B").size
+  assert_equal 3, ('A' "B" 'C').size
+  assert_equal 4, (%(A) "B#{?C}" "D").size
 end
index 9059f45..5c674a9 100644 (file)
@@ -13,8 +13,8 @@ assert('Symbol', '15.2.11') do
 end
 
 assert('Symbol#===', '15.2.11.3.1') do
-  assert_true :abc == :abc
-  assert_false :abc == :cba
+  assert_true :abc === :abc
+  assert_false :abc === :cba
 end
 
 assert('Symbol#id2name', '15.2.11.3.2') do
@@ -28,3 +28,7 @@ end
 assert('Symbol#to_sym', '15.2.11.3.4') do
   assert_equal :abc, :abc.to_sym
 end
+
+assert('Symbol#to_proc') do
+  assert_equal 5, :abs.to_proc[-5]
+end
index 2993945..603547c 100644 (file)
@@ -403,6 +403,9 @@ assert('External command execution.') do
     assert_equal 'test dynamic `', t
     assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results
 
+    results = []
+    assert_equal 'test sym test sym test', `test #{:sym} test #{:sym} test`
+
     alias_method sym, :old_cmd
   end
   true
@@ -451,18 +454,203 @@ assert('multiline comments work correctly') do
 =begin
 this is a comment with nothing after begin and end
 =end
-=begin  this is a comment 
+=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  
+=end
 =begin this is a comment
 this is a comment that has extra after =begin and =end with spaces after it
-=end  
+=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
+
+assert 'keyword arguments' do
+  def m(a, b:1) [a, b] end
+  assert_equal [1, 1], m(1)
+  assert_equal [1, 2], m(1, b: 2)
+
+  def m(a, b:) [a, b] end
+  assert_equal [1, 2], m(1, b: 2)
+  assert_raise(ArgumentError) { m b: 1 }
+  assert_raise(ArgumentError) { m 1 }
+
+  def m(a:) a end
+  assert_equal 1, m(a: 1)
+  assert_raise(ArgumentError) { m }
+  assert_raise(ArgumentError) { m 'a'  => 1, a: 1 }
+  h = { a: 1 }
+  assert_equal 1, m(h)
+  assert_equal({ a: 1 }, h)
+
+  def m(a: 1) a end
+  assert_equal 1, m
+  assert_equal 2, m(a: 2)
+  assert_raise(ArgumentError) { m 1 }
+
+  def m(**) end
+  assert_nil m
+  assert_nil m a: 1, b: 2
+  assert_raise(ArgumentError) { m 2 }
+
+  def m(a, **) a end
+  assert_equal 1, m(1)
+  assert_equal 1, m(1, a: 2, b: 3)
+  assert_equal({ 'a' => 1, b: 2 }, m('a' => 1, b: 2))
+
+  def m(a, **k) [a, k] end
+  assert_equal [1, {}], m(1)
+  assert_equal [1, {a: 2, b: 3}], m(1, a: 2, b: 3)
+  assert_equal [{'a' => 1, b: 2}, {}], m('a' => 1, b: 2)
+
+  def m(a=1, **) a end
+  assert_equal 1, m
+  assert_equal 2, m(2, a: 1, b: 0)
+  assert_raise(ArgumentError) { m('a' => 1, a: 2) }
+
+  def m(a=1, **k) [a, k] end
+  assert_equal [1, {}], m
+  assert_equal [1, {a: 1}], m(a: 1)
+  assert_equal [2, {a: 1, b: 2}], m(2, a: 1, b: 2)
+  assert_equal [{a: 1}, {b: 2}], m({a: 1}, {b: 2})
+
+  def m(*, a:) a end
+  assert_equal 1, m(a: 1)
+  assert_equal 3, m(1, 2, a: 3)
+  assert_raise(ArgumentError) { m('a' => 1, a: 2) }
+
+  def m(*a, b:) [a, b] end
+  assert_equal [[], 1], m(b: 1)
+  assert_equal [[1, 2], 3], m(1, 2, b: 3)
+  assert_raise(ArgumentError) { m('a' => 1, b: 2) }
+
+  def m(*a, b: 1) [a, b] end
+  assert_equal [[], 1], m
+  assert_equal [[1, 2, 3], 4], m(1, 2, 3, b: 4)
+  assert_raise(ArgumentError) { m('a' => 1, b: 2) }
+
+  def m(*, **) end
+  assert_nil m()
+  assert_nil m(a: 1, b: 2)
+  assert_nil m(1, 2, 3, a: 4, b: 5)
+
+  def m(*a, **) a end
+  assert_equal [], m()
+  assert_equal [1, 2, 3], m(1, 2, 3, a: 4, b: 5)
+  assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+  assert_equal [1], m(1, **{a: 2})
+
+  def m(*, **k) k end
+  assert_equal({}, m())
+  assert_equal({a: 4, b: 5}, m(1, 2, 3, a: 4, b: 5))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+
+  def m(a = nil, b = nil, **k) [a, k] end
+  assert_equal [nil, {}], m()
+  assert_equal([nil, {a: 1}], m(a: 1))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+  assert_equal([{"a" => 1}, {a: 1}], m({ "a" => 1 }, a: 1))
+  assert_equal([{a: 1}, {}], m({a: 1}, {}))
+  assert_equal([nil, {}], m({}))
+
+  def m(*a, **k) [a, k] end
+  assert_equal([[], {}], m())
+  assert_equal([[1], {}], m(1))
+  assert_equal([[], {a: 1, b: 2}], m(a: 1, b: 2))
+  assert_equal([[1, 2, 3], {a: 2}], m(1, 2, 3, a: 2))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+  assert_raise(ArgumentError) { m("a" => 1) }
+  assert_equal([[], {a: 1}], m(a: 1))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+  assert_equal([[{"a" => 1}], {a: 1}], m({ "a" => 1 }, a: 1))
+  assert_equal([[{a: 1}], {}], m({a: 1}, {}))
+  assert_raise(ArgumentError) { m({a: 1}, {"a" => 1}) }
+
+  def m(a:, b:) [a, b] end
+  assert_equal([1, 2], m(a: 1, b: 2))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+  def m(a:, b: 1) [a, b] end
+  assert_equal([1, 1], m(a: 1))
+  assert_equal([1, 2], m(a: 1, b: 2))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+  def m(a:, **) a end
+  assert_equal(1, m(a: 1))
+  assert_equal(1, m(a: 1, b: 2))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+  def m(a:, **k) [a, k] end
+  assert_equal([1, {}], m(a: 1))
+  assert_equal([1, {b: 2, c: 3}], m(a: 1, b: 2, c: 3))
+  assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+=begin
+  def m(a:, &b) [a, b] end
+  assert_equal([1, nil], m(a: 1))
+  assert_equal([1, l], m(a: 1, &(l = ->{})))
+=end
+
+  def m(a: 1, b:) [a, b] end
+  assert_equal([1, 0], m(b: 0))
+  assert_equal([3, 2], m(b: 2, a: 3))
+  assert_raise(ArgumentError) { m a: 1 }
+
+  def m(a: def m(a: 1) a end, b:)
+    [a, b]
+  end
+  assert_equal([2, 3], m(a: 2, b: 3))
+  assert_equal([:m, 1], m(b: 1))
+  # Note the default value of a: in the original method.
+  assert_equal(1, m())
+
+  def m(a: 1, b: 2) [a, b] end
+  assert_equal([1, 2], m())
+  assert_equal([4, 3], m(b: 3, a: 4))
+
+  def m(a: 1, **) a end
+  assert_equal(1, m())
+  assert_equal(2, m(a: 2, b: 1))
+
+  def m(a: 1, **k) [a, k] end
+  assert_equal([1, {b: 2, c: 3}], m(b: 2, c: 3))
+
+  def m(a:, **) yield end
+  assert_raise(ArgumentError) { m { :blk } }
+  assert_equal :blk, m(a: 1){ :blk }
+
+  def m(a:, **k, &b) [b.call, k] end
+  assert_raise(ArgumentError) { m { :blk } }
+  assert_equal [:blk, {b: 2}], m(a: 1, b: 2){ :blk }
+
+  def m(**k, &b) [k, b] end
+  assert_equal([{ a: 1, b: 2}, nil], m(a: 1, b: 2))
+  assert_equal :blk, m{ :blk }[1].call
+
+  def m(hsh = {}) hsh end
+  assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2))
+  assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2))
+
+  def m(hsh) hsh end
+  assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2))
+  assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2))
+
+=begin
+  def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l)
+    [a, b, c, d, e, f, g, h, k, l]
+  end
+  result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
+  assert_equal([9, 8, [7], [], 6, 5, 4, 3, {}, l], result)
+
+  def m a, b=1, *c, d, e:, f: 2, g:, **k, &l
+    [a, b, c, d, e, f, g, k, l]
+  end
+  result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
+  assert_equal([1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l], result)
+=end
+end
index 5904d68..e12bae6 100644 (file)
@@ -40,7 +40,7 @@ MRuby::Build.new('cxx_abi') do |conf|
   toolchain :gcc
 
   conf.gembox 'full-core'
-  conf.cc.flags += %w(-Werror=declaration-after-statement)
+  conf.cc.flags += %w(-Werror=declaration-after-statement -fpermissive)
   conf.compilers.each do |c|
     c.defines += %w(MRB_GC_FIXED_ARENA)
   end
index 420665e..a090e5c 100644 (file)
@@ -1206,12 +1206,17 @@ Redo:
     _exit(0);
 }
 
+static int (*rsa_finish)(RSA *rsa);
+
 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);
+
+    rsa_finish(rsa);
+
     if (exdata == NULL)
         return 1;
 
@@ -1406,6 +1411,8 @@ int neverbleed_init(neverbleed_t *nb, char *errbuf)
     const EC_KEY_METHOD *ecdsa_default_method;
     RSA_METHOD *rsa_method = RSA_meth_dup(RSA_PKCS1_OpenSSL());
 
+    rsa_finish = RSA_meth_get_finish(rsa_method);
+
     RSA_meth_set1_name(rsa_method, "privsep RSA method");
     RSA_meth_set_priv_enc(rsa_method, priv_enc_proxy);
     RSA_meth_set_priv_dec(rsa_method, priv_dec_proxy);
@@ -1423,6 +1430,8 @@ int neverbleed_init(neverbleed_t *nb, char *errbuf)
     const RSA_METHOD *default_method = RSA_PKCS1_SSLeay();
     RSA_METHOD *rsa_method = &static_rsa_method;
 
+    rsa_finish = default_method->finish;
+
     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;
diff --git a/third-party/url-parser/url_parser.c b/third-party/url-parser/url_parser.c
new file mode 100644 (file)
index 0000000..4912ee2
--- /dev/null
@@ -0,0 +1,652 @@
+/* Copyright Joyent, Inc. and other Node 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.
+ */
+#include "url_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef BIT_AT
+# define BIT_AT(a, i)                                                \
+  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
+   (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#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_http_major
+  , s_res_http_dot
+  , s_res_http_minor
+  , s_res_http_end
+  , 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_http_I
+  , s_req_http_IC
+  , s_req_http_major
+  , s_req_http_dot
+  , s_req_http_minor
+  , s_req_http_end
+  , 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
+  };
+
+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)     ((c == ' ') ? 0 : tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c)            STRICT_TOKEN(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)            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
+
+/* 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;
+      }
+
+    /* fall through */
+    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;
+}
+
+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;
+      }
+
+    /* fall through */
+    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;
+      }
+
+    /* fall through */
+    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;
+      }
+
+    /* fall through */
+    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 = (uint16_t)(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 = (uint16_t)(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 = (uint16_t)(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 = (uint16_t)(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;
+
+  if (buflen == 0) {
+    return 1;
+  }
+
+  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;
+
+      /* fall through */
+      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 = (uint16_t)(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)) {
+    uint16_t off;
+    uint16_t len;
+    const char* p;
+    const char* end;
+    unsigned long v;
+
+    off = u->field_data[UF_PORT].off;
+    len = u->field_data[UF_PORT].len;
+    end = buf + off + len;
+
+    /* NOTE: The characters are already validated and are in the [0-9] range */
+    assert(off + len <= buflen && "Port number overflow");
+    v = 0;
+    for (p = buf + off; p < end; p++) {
+      v *= 10;
+      v += *p - '0';
+
+      /* Ports have a max value of 2^16 */
+      if (v > 0xffff) {
+        return 1;
+      }
+    }
+
+    u->port = (uint16_t) v;
+  }
+
+  return 0;
+}
diff --git a/third-party/url-parser/url_parser.h b/third-party/url-parser/url_parser.h
new file mode 100644 (file)
index 0000000..78b3096
--- /dev/null
@@ -0,0 +1,94 @@
+/* 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 url_parser_h
+#define url_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 9
+#define HTTP_PARSER_VERSION_PATCH 1
+
+#include <stddef.h>
+#if defined(_WIN32) && !defined(__MINGW32__) && \
+  (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
+#include <BaseTsd.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
+
+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];
+};
+
+/* 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);
+#ifdef __cplusplus
+}
+#endif
+#endif