From b485aab7e71c1625cfc27e0f92c9509f42378458 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 5 May 2024 13:19:16 +0200 Subject: Adding upstream version 1.45.3+dfsg. Signed-off-by: Daniel Baumann --- src/web/server/h2o/libh2o/misc/build_libFuzzer.sh | 8 + .../h2o/libh2o/misc/cache-digest.js/.travis.yml | 5 + .../h2o/libh2o/misc/cache-digest.js/README.md | 33 + .../libh2o/misc/cache-digest.js/cache-digest.js | 247 + .../server/h2o/libh2o/misc/cache-digest.js/cli.js | 74 + .../server/h2o/libh2o/misc/cache-digest.js/test.js | 72 + src/web/server/h2o/libh2o/misc/clang-format-all.sh | 4 + .../h2o/libh2o/misc/dump-github-repository.pl | 41 + src/web/server/h2o/libh2o/misc/embed_mruby_code.pl | 54 + src/web/server/h2o/libh2o/misc/fastcgi-cgi.pl | 256 + .../server/h2o/libh2o/misc/install-perl-module.pl | 27 + .../server/h2o/libh2o/misc/libressl-2.4.5.tar.gz | Bin 0 -> 3016462 bytes src/web/server/h2o/libh2o/misc/libressl.mk | 12 + src/web/server/h2o/libh2o/misc/makedoc.pl | 69 + src/web/server/h2o/libh2o/misc/mk-ca-bundle.pl | 499 + src/web/server/h2o/libh2o/misc/mkhufftbl.py | 553 + .../server/h2o/libh2o/misc/mruby-mtest/.gitignore | 11 + .../server/h2o/libh2o/misc/mruby-mtest/.travis.yml | 2 + .../server/h2o/libh2o/misc/mruby-mtest/README.md | 63 + .../h2o/libh2o/misc/mruby-mtest/example/sample.rb | 46 + .../server/h2o/libh2o/misc/mruby-mtest/mrbgem.rake | 8 + .../libh2o/misc/mruby-mtest/mrblib/mtest_unit.rb | 698 + .../server/h2o/libh2o/misc/mruby-mtest/run_test.rb | 26 + .../misc/mruby-mtest/test/mtest_unit_test.rb | 74 + src/web/server/h2o/libh2o/misc/mruby_config.rb | 29 + src/web/server/h2o/libh2o/misc/oktavia/.gitignore | 1 + src/web/server/h2o/libh2o/misc/oktavia/.proverc | 4 + src/web/server/h2o/libh2o/misc/oktavia/.travis.yml | 12 + src/web/server/h2o/libh2o/misc/oktavia/LICENSE.rst | 22 + src/web/server/h2o/libh2o/misc/oktavia/Makefile | 54 + src/web/server/h2o/libh2o/misc/oktavia/README.rst | 78 + .../server/h2o/libh2o/misc/oktavia/bin/httpstatus | 7032 ++++++ .../h2o/libh2o/misc/oktavia/bin/oktavia-mkindex | 25034 +++++++++++++++++++ .../h2o/libh2o/misc/oktavia/bin/oktavia-search | 24779 ++++++++++++++++++ .../misc/oktavia/lib/oktavia-danish-search.js | 8023 ++++++ .../misc/oktavia/lib/oktavia-dutch-search.js | 8423 +++++++ .../misc/oktavia/lib/oktavia-english-search.js | 8922 +++++++ .../misc/oktavia/lib/oktavia-finnish-search.js | 8609 +++++++ .../misc/oktavia/lib/oktavia-french-search.js | 9061 +++++++ .../misc/oktavia/lib/oktavia-german-search.js | 8352 +++++++ .../misc/oktavia/lib/oktavia-hungarian-search.js | 8599 +++++++ .../misc/oktavia/lib/oktavia-italian-search.js | 8633 +++++++ .../misc/oktavia/lib/oktavia-jquery-highlight.js | 105 + .../libh2o/misc/oktavia/lib/oktavia-jquery-ui.js | 521 + .../misc/oktavia/lib/oktavia-norwegian-search.js | 7952 ++++++ .../misc/oktavia/lib/oktavia-porter-search.js | 8494 +++++++ .../misc/oktavia/lib/oktavia-portuguese-search.js | 8566 +++++++ .../misc/oktavia/lib/oktavia-romanian-search.js | 8488 +++++++ .../misc/oktavia/lib/oktavia-russian-search.js | 8254 ++++++ .../h2o/libh2o/misc/oktavia/lib/oktavia-search.js | 6795 +++++ .../misc/oktavia/lib/oktavia-spanish-search.js | 8607 +++++++ .../misc/oktavia/lib/oktavia-swedish-search.js | 7934 ++++++ .../misc/oktavia/lib/oktavia-turkish-search.js | 10729 ++++++++ .../h2o/libh2o/misc/oktavia/lib/searchstyle.css | 174 + .../server/h2o/libh2o/misc/oktavia/package.json | 23 + .../h2o/libh2o/misc/oktavia/resource/favicon.ai | 1176 + .../h2o/libh2o/misc/oktavia/resource/oktavia.eps | Bin 0 -> 702054 bytes .../libh2o/misc/oktavia/resource/oktavia_large.jpg | Bin 0 -> 91592 bytes .../misc/oktavia/resource/oktavia_medium.jpg | Bin 0 -> 38493 bytes .../libh2o/misc/oktavia/resource/oktavia_small.jpg | Bin 0 -> 39534 bytes .../h2o/libh2o/misc/oktavia/src/binary-util.jsx | 597 + .../h2o/libh2o/misc/oktavia/src/bit-vector.jsx | 295 + .../misc/oktavia/src/burrows-wheeler-transform.jsx | 73 + .../h2o/libh2o/misc/oktavia/src/csvparser.jsx | 22 + .../h2o/libh2o/misc/oktavia/src/fm-index.jsx | 323 + .../server/h2o/libh2o/misc/oktavia/src/getopt.jsx | 356 + .../h2o/libh2o/misc/oktavia/src/htmlparser.jsx | 280 + .../h2o/libh2o/misc/oktavia/src/metadata.jsx | 498 + .../h2o/libh2o/misc/oktavia/src/node-sqlite3.jsx | 115 + .../server/h2o/libh2o/misc/oktavia/src/oktavia.jsx | 427 + .../h2o/libh2o/misc/oktavia/src/query-parser.jsx | 60 + .../misc/oktavia/src/query-string-parser.jsx | 128 + .../server/h2o/libh2o/misc/oktavia/src/query.jsx | 37 + .../server/h2o/libh2o/misc/oktavia/src/sais.jsx | 250 + src/web/server/h2o/libh2o/misc/oktavia/src/sax.jsx | 1356 + .../h2o/libh2o/misc/oktavia/src/search-result.jsx | 287 + .../h2o/libh2o/misc/oktavia/src/stemmer/among.jsx | 34 + .../misc/oktavia/src/stemmer/base-stemmer.jsx | 419 + .../misc/oktavia/src/stemmer/danish-stemmer.jsx | 507 + .../misc/oktavia/src/stemmer/dutch-stemmer.jsx | 1020 + .../misc/oktavia/src/stemmer/english-stemmer.jsx | 1638 ++ .../misc/oktavia/src/stemmer/finnish-stemmer.jsx | 1208 + .../misc/oktavia/src/stemmer/french-stemmer.jsx | 1867 ++ .../misc/oktavia/src/stemmer/german-stemmer.jsx | 894 + .../misc/oktavia/src/stemmer/hungarian-stemmer.jsx | 1478 ++ .../misc/oktavia/src/stemmer/italian-stemmer.jsx | 1412 ++ .../misc/oktavia/src/stemmer/norwegian-stemmer.jsx | 428 + .../misc/oktavia/src/stemmer/porter-stemmer.jsx | 1121 + .../oktavia/src/stemmer/portuguese-stemmer.jsx | 1321 + .../misc/oktavia/src/stemmer/romanian-stemmer.jsx | 1227 + .../misc/oktavia/src/stemmer/russian-stemmer.jsx | 875 + .../misc/oktavia/src/stemmer/spanish-stemmer.jsx | 1408 ++ .../libh2o/misc/oktavia/src/stemmer/stemmer.jsx | 5 + .../misc/oktavia/src/stemmer/swedish-stemmer.jsx | 416 + .../misc/oktavia/src/stemmer/turkish-stemmer.jsx | 3824 +++ .../server/h2o/libh2o/misc/oktavia/src/style.jsx | 105 + .../h2o/libh2o/misc/oktavia/src/textparser.jsx | 25 + .../h2o/libh2o/misc/oktavia/src/tiny-segmenter.jsx | 350 + .../h2o/libh2o/misc/oktavia/src/wavelet-matrix.jsx | 321 + .../libh2o/misc/oktavia/templates/jsdoc3/README.md | 1 + .../misc/oktavia/templates/jsdoc3/publish.js | 508 + .../jsdoc3/static/scripts/jquery-1.9.1.min.js | 5 + .../static/scripts/oktavia-english-search.js | 8922 +++++++ .../static/scripts/oktavia-jquery-highlight.js | 105 + .../jsdoc3/static/scripts/oktavia-jquery-ui.js | 521 + .../static/scripts/prettify/Apache-License-2.0.txt | 202 + .../jsdoc3/static/scripts/prettify/lang-css.js | 2 + .../jsdoc3/static/scripts/prettify/prettify.js | 28 + .../jsdoc3/static/styles/jsdoc-default.css | 283 + .../jsdoc3/static/styles/prettify-jsdoc.css | 111 + .../jsdoc3/static/styles/prettify-tomorrow.css | 132 + .../templates/jsdoc3/static/styles/searchstyle.css | 174 + .../oktavia/templates/jsdoc3/tmpl/container.tmpl | 154 + .../oktavia/templates/jsdoc3/tmpl/details.tmpl | 98 + .../oktavia/templates/jsdoc3/tmpl/example.tmpl | 2 + .../oktavia/templates/jsdoc3/tmpl/examples.tmpl | 11 + .../oktavia/templates/jsdoc3/tmpl/exceptions.tmpl | 19 + .../misc/oktavia/templates/jsdoc3/tmpl/fires.tmpl | 4 + .../misc/oktavia/templates/jsdoc3/tmpl/layout.tmpl | 45 + .../oktavia/templates/jsdoc3/tmpl/mainpage.tmpl | 14 + .../oktavia/templates/jsdoc3/tmpl/members.tmpl | 22 + .../misc/oktavia/templates/jsdoc3/tmpl/method.tmpl | 76 + .../misc/oktavia/templates/jsdoc3/tmpl/params.tmpl | 108 + .../oktavia/templates/jsdoc3/tmpl/properties.tmpl | 107 + .../oktavia/templates/jsdoc3/tmpl/returns.tmpl | 19 + .../misc/oktavia/templates/jsdoc3/tmpl/source.tmpl | 8 + .../oktavia/templates/jsdoc3/tmpl/tutorial.tmpl | 19 + .../misc/oktavia/templates/jsdoc3/tmpl/type.tmpl | 7 + .../templates/sphinx/_static/oktavia-jquery-ui.js | 521 + .../templates/sphinx/_static/oktavia-search.js | 6795 +++++ .../templates/sphinx/_static/searchstyle.css | 174 + .../templates/sphinx/_templates/layout.html | 12 + .../templates/sphinx/_templates/searchbox.html | 0 .../tinkerer/_static/oktavia-jquery-ui.js | 521 + .../templates/tinkerer/_static/oktavia-search.js | 6795 +++++ .../templates/tinkerer/_static/searchstyle.css | 174 + .../tinkerer/_templates/oktaviasearch.html | 8 + .../libh2o/misc/oktavia/test/test-binary-util.jsx | 190 + .../libh2o/misc/oktavia/test/test-bit-vector.jsx | 131 + .../test/test-burrows-wheeler-transform.jsx | 24 + .../h2o/libh2o/misc/oktavia/test/test-fm-index.jsx | 250 + .../h2o/libh2o/misc/oktavia/test/test-getopt.jsx | 84 + .../misc/oktavia/test/test-oktavia-block.jsx | 226 + .../misc/oktavia/test/test-oktavia-section.jsx | 235 + .../misc/oktavia/test/test-oktavia-splitter.jsx | 173 + .../misc/oktavia/test/test-oktavia-stemming.jsx | 55 + .../misc/oktavia/test/test-oktavia-table.jsx | 213 + .../libh2o/misc/oktavia/test/test-query-parser.jsx | 92 + .../misc/oktavia/test/test-query-string-parser.jsx | 94 + .../h2o/libh2o/misc/oktavia/test/test-sax.jsx | 147 + .../misc/oktavia/test/test-search-result.jsx | 159 + .../misc/oktavia/test/test-wavelet-matrix.jsx | 143 + .../libh2o/misc/oktavia/testdata/jsx_literal.txt | 65 + .../libh2o/misc/oktavia/testdata/jsx_operator.txt | 68 + .../misc/oktavia/testdata/jsx_primitive_type.txt | 68 + .../libh2o/misc/oktavia/testdata/jsx_tutorial.txt | 213 + .../misc/oktavia/testdata/jsx_typeconversion.txt | 40 + .../h2o/libh2o/misc/oktavia/tool/httpstatus.jsx | 130 + .../libh2o/misc/oktavia/tool/oktavia-mkindex.jsx | 470 + .../libh2o/misc/oktavia/tool/oktavia-search.jsx | 370 + .../h2o/libh2o/misc/oktavia/tool/search_simple.jsx | 39 + .../oktavia/tool/web/oktavia-danish-search.jsx | 10 + .../misc/oktavia/tool/web/oktavia-dutch-search.jsx | 10 + .../oktavia/tool/web/oktavia-english-search.jsx | 10 + .../oktavia/tool/web/oktavia-finnish-search.jsx | 10 + .../oktavia/tool/web/oktavia-french-search.jsx | 10 + .../oktavia/tool/web/oktavia-german-search.jsx | 10 + .../oktavia/tool/web/oktavia-hungarian-search.jsx | 10 + .../oktavia/tool/web/oktavia-italian-search.jsx | 10 + .../oktavia/tool/web/oktavia-norwegian-search.jsx | 10 + .../oktavia/tool/web/oktavia-porter-search.jsx | 10 + .../oktavia/tool/web/oktavia-portuguese-search.jsx | 10 + .../oktavia/tool/web/oktavia-romanian-search.jsx | 10 + .../oktavia/tool/web/oktavia-russian-search.jsx | 10 + .../misc/oktavia/tool/web/oktavia-search.jsx | 327 + .../oktavia/tool/web/oktavia-spanish-search.jsx | 10 + .../oktavia/tool/web/oktavia-swedish-search.jsx | 10 + .../oktavia/tool/web/oktavia-turkish-search.jsx | 10 + .../h2o/libh2o/misc/p5-Server-Starter/.gitignore | 32 + .../h2o/libh2o/misc/p5-Server-Starter/.travis.yml | 8 + .../h2o/libh2o/misc/p5-Server-Starter/Build.PL | 65 + .../h2o/libh2o/misc/p5-Server-Starter/Changes | 104 + .../h2o/libh2o/misc/p5-Server-Starter/LICENSE | 379 + .../h2o/libh2o/misc/p5-Server-Starter/META.json | 89 + .../h2o/libh2o/misc/p5-Server-Starter/README.md | 58 + .../misc/p5-Server-Starter/builder/MyBuilder.pm | 13 + .../h2o/libh2o/misc/p5-Server-Starter/cpanfile | 9 + .../misc/p5-Server-Starter/lib/Server/Starter.pm | 603 + .../p5-Server-Starter/lib/Server/Starter/Guard.pm | 21 + .../h2o/libh2o/misc/p5-Server-Starter/minil.toml | 2 + .../misc/p5-Server-Starter/script/start_server | 179 + .../h2o/libh2o/misc/p5-Server-Starter/t/00-base.t | 8 + .../misc/p5-Server-Starter/t/01-starter-echod.pl | 35 + .../libh2o/misc/p5-Server-Starter/t/01-starter.t | 82 + .../p5-Server-Starter/t/02-startfail-server.pl | 32 + .../libh2o/misc/p5-Server-Starter/t/02-startfail.t | 61 + .../p5-Server-Starter/t/03-starter-unix-echod.pl | 22 + .../misc/p5-Server-Starter/t/03-starter-unix.t | 40 + .../misc/p5-Server-Starter/t/04-starter-dir.t | 54 + .../p5-Server-Starter/t/05-killolddelay-echod.pl | 35 + .../misc/p5-Server-Starter/t/05-killolddelay.t | 87 + .../p5-Server-Starter/t/06-autorestart-echod.pl | 34 + .../misc/p5-Server-Starter/t/06-autorestart.t | 84 + .../misc/p5-Server-Starter/t/07-envdir-print.pl | 29 + .../libh2o/misc/p5-Server-Starter/t/07-envdir.t | 77 + .../h2o/libh2o/misc/p5-Server-Starter/t/08-wait3.t | 39 + .../h2o/libh2o/misc/p5-Server-Starter/t/09-guard.t | 31 + .../misc/p5-Server-Starter/t/10-bindaddr-server.pl | 26 + .../libh2o/misc/p5-Server-Starter/t/10-bindaddr.t | 55 + .../p5-Server-Starter/t/11-specified-fd-server.pl | 25 + .../misc/p5-Server-Starter/t/11-specified-fd.t | 40 + .../misc/p5-Server-Starter/t/12-stop-server.pl | 24 + .../h2o/libh2o/misc/p5-Server-Starter/t/12-stop.t | 50 + .../misc/p5-Server-Starter/t/13-unix-daemonize.t | 68 + .../server/h2o/libh2o/misc/p5-net-fastcgi/Changes | 102 + .../h2o/libh2o/misc/p5-net-fastcgi/MANIFEST.SKIP | 25 + .../h2o/libh2o/misc/p5-net-fastcgi/Makefile.PL | 19 + .../server/h2o/libh2o/misc/p5-net-fastcgi/README | 113 + .../h2o/libh2o/misc/p5-net-fastcgi/eg/runfcgi.pl | 226 + .../h2o/libh2o/misc/p5-net-fastcgi/eg/server.pl | 164 + .../libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm | 12 + .../libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod | 170 + .../p5-net-fastcgi/lib/Net/FastCGI/Constant.pm | 179 + .../p5-net-fastcgi/lib/Net/FastCGI/Constant.pod | 264 + .../misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm | 227 + .../misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod | 391 + .../p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm | 203 + .../p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod | 1227 + .../p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm | 429 + .../h2o/libh2o/misc/p5-net-fastcgi/t/000_load.t | 29 + .../p5-net-fastcgi/t/020_protocol/001_header.t | 51 + .../t/020_protocol/005_record_length.t | 44 + .../t/020_protocol/010_build_record.t | 44 + .../t/020_protocol/015_build_stream.t | 82 + .../t/020_protocol/020_begin_request_body.t | 41 + .../t/020_protocol/025_begin_request_record.t | 30 + .../t/020_protocol/027_begin_request.t | 97 + .../t/020_protocol/030_end_request_body.t | 42 + .../t/020_protocol/035_end_request_record.t | 30 + .../t/020_protocol/037_end_request.t | 87 + .../t/020_protocol/040_unknown_type_body.t | 42 + .../t/020_protocol/045_unknown_type_record.t | 30 + .../t/020_protocol/050_parse_record.t | 180 + .../t/020_protocol/055_parse_record_body.t | 98 + .../p5-net-fastcgi/t/020_protocol/060_params.t | 79 + .../t/020_protocol/065_record_type.t | 105 + .../misc/p5-net-fastcgi/t/020_protocol/070_names.t | 80 + .../t/020_protocol/080_dump_record.t | 51 + .../t/020_protocol/085_dump_record_body.t | 150 + .../libh2o/misc/p5-net-fastcgi/t/lib/myconfig.pm | 9 + .../h2o/libh2o/misc/p5-net-fastcgi/xt/000_pod.t | 17 + .../misc/p5-net-fastcgi/xt/010_pod_coverage.t | 29 + .../server/h2o/libh2o/misc/picotemplate-conf.pl | 21 + .../h2o/libh2o/misc/picotemplate/README.mkdn | 42 + .../h2o/libh2o/misc/picotemplate/picotemplate.pl | 172 + src/web/server/h2o/libh2o/misc/regen.mk | 45 + src/web/server/h2o/libh2o/misc/test-ca/README.md | 7 + src/web/server/h2o/libh2o/misc/test-ca/ca.crt | 17 + src/web/server/h2o/libh2o/misc/test-ca/ca.key | 27 + .../h2o/libh2o/misc/test-ca/demoCA/index.txt | 2 + .../h2o/libh2o/misc/test-ca/demoCA/index.txt.attr | 1 + .../libh2o/misc/test-ca/demoCA/newcerts/.gitkeep | 0 .../server/h2o/libh2o/misc/test-ca/demoCA/serial | 1 + src/web/server/h2o/libh2o/misc/tokens.pl | 234 + 264 files changed, 272614 insertions(+) create mode 100755 src/web/server/h2o/libh2o/misc/build_libFuzzer.sh create mode 100644 src/web/server/h2o/libh2o/misc/cache-digest.js/.travis.yml create mode 100644 src/web/server/h2o/libh2o/misc/cache-digest.js/README.md create mode 100644 src/web/server/h2o/libh2o/misc/cache-digest.js/cache-digest.js create mode 100644 src/web/server/h2o/libh2o/misc/cache-digest.js/cli.js create mode 100644 src/web/server/h2o/libh2o/misc/cache-digest.js/test.js create mode 100755 src/web/server/h2o/libh2o/misc/clang-format-all.sh create mode 100755 src/web/server/h2o/libh2o/misc/dump-github-repository.pl create mode 100755 src/web/server/h2o/libh2o/misc/embed_mruby_code.pl create mode 100755 src/web/server/h2o/libh2o/misc/fastcgi-cgi.pl create mode 100755 src/web/server/h2o/libh2o/misc/install-perl-module.pl create mode 100644 src/web/server/h2o/libh2o/misc/libressl-2.4.5.tar.gz create mode 100644 src/web/server/h2o/libh2o/misc/libressl.mk create mode 100755 src/web/server/h2o/libh2o/misc/makedoc.pl create mode 100755 src/web/server/h2o/libh2o/misc/mk-ca-bundle.pl create mode 100644 src/web/server/h2o/libh2o/misc/mkhufftbl.py create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/.gitignore create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/.travis.yml create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/README.md create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/example/sample.rb create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/mrbgem.rake create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/mrblib/mtest_unit.rb create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/run_test.rb create mode 100644 src/web/server/h2o/libh2o/misc/mruby-mtest/test/mtest_unit_test.rb create mode 100644 src/web/server/h2o/libh2o/misc/mruby_config.rb create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/.gitignore create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/.proverc create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/.travis.yml create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/LICENSE.rst create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/Makefile create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/README.rst create mode 100755 src/web/server/h2o/libh2o/misc/oktavia/bin/httpstatus create mode 100755 src/web/server/h2o/libh2o/misc/oktavia/bin/oktavia-mkindex create mode 100755 src/web/server/h2o/libh2o/misc/oktavia/bin/oktavia-search create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-danish-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-dutch-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-english-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-finnish-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-french-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-german-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-hungarian-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-italian-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-jquery-highlight.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-jquery-ui.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-norwegian-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-porter-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-portuguese-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-romanian-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-russian-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-spanish-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-swedish-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/oktavia-turkish-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/lib/searchstyle.css create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/package.json create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/resource/favicon.ai create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/resource/oktavia.eps create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/resource/oktavia_large.jpg create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/resource/oktavia_medium.jpg create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/resource/oktavia_small.jpg create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/binary-util.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/bit-vector.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/burrows-wheeler-transform.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/csvparser.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/fm-index.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/getopt.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/htmlparser.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/metadata.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/node-sqlite3.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/oktavia.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/query-parser.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/query-string-parser.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/query.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/sais.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/sax.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/search-result.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/among.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/base-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/danish-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/dutch-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/english-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/finnish-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/french-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/german-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/hungarian-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/italian-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/norwegian-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/porter-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/portuguese-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/romanian-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/russian-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/spanish-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/swedish-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/stemmer/turkish-stemmer.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/style.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/textparser.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/tiny-segmenter.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/src/wavelet-matrix.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/README.md create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/publish.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/scripts/jquery-1.9.1.min.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/scripts/oktavia-english-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/scripts/oktavia-jquery-highlight.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/scripts/oktavia-jquery-ui.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/scripts/prettify/Apache-License-2.0.txt create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/scripts/prettify/lang-css.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/scripts/prettify/prettify.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/styles/jsdoc-default.css create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/styles/prettify-jsdoc.css create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/styles/prettify-tomorrow.css create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/static/styles/searchstyle.css create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/container.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/details.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/example.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/examples.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/exceptions.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/fires.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/layout.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/mainpage.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/members.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/method.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/params.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/properties.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/returns.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/source.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/tutorial.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/type.tmpl create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-jquery-ui.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/searchstyle.css create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_templates/layout.html create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_templates/searchbox.html create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-jquery-ui.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-search.js create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/searchstyle.css create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_templates/oktaviasearch.html create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-binary-util.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-bit-vector.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-burrows-wheeler-transform.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-fm-index.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-getopt.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-block.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-section.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-splitter.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-stemming.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-table.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-query-parser.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-query-string-parser.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-sax.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-search-result.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/test/test-wavelet-matrix.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_literal.txt create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_operator.txt create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_primitive_type.txt create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_tutorial.txt create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_typeconversion.txt create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/httpstatus.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-mkindex.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/search_simple.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-danish-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-dutch-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-english-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-finnish-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-french-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-german-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-hungarian-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-italian-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-norwegian-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-porter-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-portuguese-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-romanian-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-russian-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-spanish-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-swedish-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-turkish-search.jsx create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/.gitignore create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/.travis.yml create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/Build.PL create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/Changes create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/LICENSE create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/META.json create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/README.md create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/builder/MyBuilder.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/cpanfile create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter/Guard.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/minil.toml create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/script/start_server create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/00-base.t create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter-echod.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter.t create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail-server.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix-echod.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/04-starter-dir.t create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay-echod.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay.t create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart-echod.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart.t create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir-print.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/08-wait3.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/09-guard.t create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr-server.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd-server.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd.t create mode 100755 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop-server.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/13-unix-daemonize.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Changes create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/MANIFEST.SKIP create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Makefile.PL create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/README create mode 100755 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/runfcgi.pl create mode 100755 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/server.pl create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pod create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/000_load.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/001_header.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/005_record_length.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/010_build_record.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/015_build_stream.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/020_begin_request_body.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/025_begin_request_record.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/027_begin_request.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/030_end_request_body.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/035_end_request_record.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/037_end_request.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/040_unknown_type_body.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/045_unknown_type_record.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/050_parse_record.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/055_parse_record_body.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/060_params.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/065_record_type.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/070_names.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/080_dump_record.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/085_dump_record_body.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/lib/myconfig.pm create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/000_pod.t create mode 100644 src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/010_pod_coverage.t create mode 100644 src/web/server/h2o/libh2o/misc/picotemplate-conf.pl create mode 100644 src/web/server/h2o/libh2o/misc/picotemplate/README.mkdn create mode 100755 src/web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl create mode 100755 src/web/server/h2o/libh2o/misc/regen.mk create mode 100644 src/web/server/h2o/libh2o/misc/test-ca/README.md create mode 100644 src/web/server/h2o/libh2o/misc/test-ca/ca.crt create mode 100644 src/web/server/h2o/libh2o/misc/test-ca/ca.key create mode 100644 src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt create mode 100644 src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt.attr create mode 100644 src/web/server/h2o/libh2o/misc/test-ca/demoCA/newcerts/.gitkeep create mode 100644 src/web/server/h2o/libh2o/misc/test-ca/demoCA/serial create mode 100755 src/web/server/h2o/libh2o/misc/tokens.pl (limited to 'src/web/server/h2o/libh2o/misc') diff --git a/src/web/server/h2o/libh2o/misc/build_libFuzzer.sh b/src/web/server/h2o/libh2o/misc/build_libFuzzer.sh new file mode 100755 index 000000000..67a64c9ac --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/build_libFuzzer.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +[ -e libFuzzer.a ] && exit 0 +[ -d Fuzzer ] || git clone https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer +cd Fuzzer +git checkout 29d1659edabe4ba2396f9697915bb7a0880cbd2f +cd .. +Fuzzer/build.sh diff --git a/src/web/server/h2o/libh2o/misc/cache-digest.js/.travis.yml b/src/web/server/h2o/libh2o/misc/cache-digest.js/.travis.yml new file mode 100644 index 000000000..7c774e43f --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/cache-digest.js/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "6" +script: + - node test.js diff --git a/src/web/server/h2o/libh2o/misc/cache-digest.js/README.md b/src/web/server/h2o/libh2o/misc/cache-digest.js/README.md new file mode 100644 index 000000000..bb0649ddd --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/cache-digest.js/README.md @@ -0,0 +1,33 @@ +cache-digest.js +====== + +[![Build Status](https://travis-ci.org/h2o/cache-digest.js.svg?branch=master)](https://travis-ci.org/h2o/cache-digest.js) + +[Service Worker](https://developer.mozilla.org/docs/Web/API/Service_Worker_API) implementation of [Cache Digests for HTTP/2 (draft 01)](https://tools.ietf.org/html/draft-kazuho-h2-cache-digest-01) + +Warning +------ + +* WIP; the code is in early-beta stage +* only supports sending of _fresh_ digests without etag + +How to Use +------ + +1. install cache-digest.js into the root directory of the website +2. add `` to your web pages +3. adjust the web server configuration to send: + * `service-worker-allowed: /` response header + * `link: ; rel="preload"` response header (see [spec](https://w3c.github.io/preload/)) + +Calculating Digests at Command Line +------ + +You can run cli.js to calculate cache digests manually. + +``` +% node cli.js -b https://example.com/style.css https://example.com/jquery.js https://example.com/shortcut.css +EdcLLJA +``` + +In the above example, `-b` option is used so that the digest would be encoded using [base64url](https://tools.ietf.org/html/rfc4648#section-5). Please refer to `-h` (help) option for more information. diff --git a/src/web/server/h2o/libh2o/misc/cache-digest.js/cache-digest.js b/src/web/server/h2o/libh2o/misc/cache-digest.js/cache-digest.js new file mode 100644 index 000000000..b972a6cab --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/cache-digest.js/cache-digest.js @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2015,2016 Jxck, DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * + * Includes a minified SHA256 implementation taken from https://gist.github.com/kazuho/bb8aab1a2946bbf42127d8a6197ad18c, + * licensed under the following copyright: + * + * Copyright (c) 2015,2016 Chen Yi-Cyuan, Kazuho Oku + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +"use strict"; + +if (typeof self !== "undefined" && "ServiceWorkerGlobalScope" in self && + self instanceof ServiceWorkerGlobalScope) { + + /* ServiceWorker */ + self.addEventListener('fetch', function(evt) { + var req = evt.request.clone(); + if (req.method != "GET" || req.url.match(/\/cache-digests?\.js(?:\?|$)/)) { + logInfo(req, "skip"); + return; + } + evt.respondWith(caches.open("v1").then(function (cache) { + return cache.match(req).then(function (res) { + if (res && isFresh(res.headers.entries(), Date.now())) { + logInfo(req, "hit"); + return res; + } + var requestWithDigests = function (digests) { + if (digests != null) { + var err = null; + try { + req = new Request(req); + req.headers.append("cache-digest", digests); + if (req.headers.get("cache-digest") == null) + err = "append failed"; + } catch (e) { + err = e; + } + if (err) + logError(req, e); + } + return fetch(req).then(function (res) { + var cached = false; + if (res.status == 200 && isFresh(res.headers.entries(), Date.now())) { + cache.put(req, res.clone()); + cached = true; + } + logInfo(req, "fetched" + (cached ? " & cached" : "") + " with cache-digest:\"" + digests + "\""); + return res; + }); + }; + if (req.mode == "navigate") { + return generateCacheDigests(cache).then(requestWithDigests); + } else { + return requestWithDigests(null); + } + }); + })); + }); + +} else if (typeof navigator !== "undefined") { + + /* bootstrap, loaded via + + + + + + + + + + + + + + +
+ +

+ + +
+ + + +
+ +
+ Documentation generated by JSDoc on +
+ + + + diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/mainpage.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/mainpage.tmpl new file mode 100644 index 000000000..64e9e5943 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/mainpage.tmpl @@ -0,0 +1,14 @@ + + + +

+ + + +
+
+
+ diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/members.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/members.tmpl new file mode 100644 index 000000000..c76f772cc --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/members.tmpl @@ -0,0 +1,22 @@ + +
+

+ + +

+ +
+
+ +
+ +
+ + + + + +
Example 1? 's':'' ?>
+ + +
diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/method.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/method.tmpl new file mode 100644 index 000000000..1eb9032d2 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/method.tmpl @@ -0,0 +1,76 @@ + +
+

+ + +

+ +
+
+ + +
+ +
+ + + +
Type:
+
    +
  • + +
  • +
+ + + +
This:
+
+ + + +
Parameters:
+ + + + + + +
Fires:
+
    + +
+ + + +
Throws:
+ 1) { ?>
    +
  • +
+ + + + +
Returns:
+ 1) { ?>
    +
  • +
+ + + + +
Example 1? 's':'' ?>
+ + +
diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/params.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/params.tmpl new file mode 100644 index 000000000..ca81f8f8f --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/params.tmpl @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
+ + + + + + <optional>
+ + + + <nullable>
+ +
+ + + + +
Properties
+ +
\ No newline at end of file diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/properties.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/properties.tmpl new file mode 100644 index 000000000..dfa6ee0e9 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/properties.tmpl @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
+ + + + + + <optional>
+ + + + <nullable>
+ +
+ + + + +
Properties
+
\ No newline at end of file diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/returns.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/returns.tmpl new file mode 100644 index 000000000..32e059ed4 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/returns.tmpl @@ -0,0 +1,19 @@ + +
+ +
+ + + +
+
+ Type +
+
+ +
+
+ \ No newline at end of file diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/source.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/source.tmpl new file mode 100644 index 000000000..e1092ef29 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/source.tmpl @@ -0,0 +1,8 @@ + +
+
+
+
+
\ No newline at end of file diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/tutorial.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/tutorial.tmpl new file mode 100644 index 000000000..b0c79c1dd --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/tutorial.tmpl @@ -0,0 +1,19 @@ +
+ +
+ 0) { ?> +
    +
  • +
+ + +

+
+ +
+ +
+ +
diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/type.tmpl b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/type.tmpl new file mode 100644 index 000000000..db8194d6d --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/jsdoc3/tmpl/type.tmpl @@ -0,0 +1,7 @@ + + +| + \ No newline at end of file diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-jquery-ui.js b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-jquery-ui.js new file mode 100644 index 000000000..bd1efb8c5 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-jquery-ui.js @@ -0,0 +1,521 @@ +/** + * @fileOverview + * A UI script that creates search form, loads an index files and show search results. + * It needs jQuery and oktavia-search.js or oktavia-*-search.js + * (stemming library supported versions). + * @author Yoshiki Shibukawa, yoshiki@shibu.jp + */ + +(function ($) +{ + var logosrc; + /** + * @name SearchView + * @class + * Provides searching feature to your website. + * @constructor + * @param {jQeury} node Target node it has a search form and a search result window. + * @param {string} documentRoot Document root folder like '.', '../', '/' + * @param {string} index Index file path. + */ + function SearchView(node, documentRoot, index) + { + var OktaviaSearch = JSX.require("tool/web/oktavia-search.jsx").OktaviaSearch$I; + + /** + * Target node it contains a search form and a search result window. + * @type jQuery + */ + this.node = node; + /** + * Search engine core + * @type OktaviaSearch + */ + this.engine = new OktaviaSearch(5); + if (documentRoot === '') + { + /** + * Document root path + * @type string[] + */ + this.documentRoot = []; + } + else if (documentRoot.slice(-1) === '/') + { + this.documentRoot = documentRoot.slice(0, -1).split(/\//g); + } + else + { + this.documentRoot = documentRoot.split(/\//g); + } + + /** + * It is true if an index file is loaded. + * @type boolean + */ + this.initialized = false; + /** + * It is true if an user search before loading an index. + * @type boolean + */ + this.reserveSearch = false; + + var indexURL; + switch (index.charAt(0)) + { + case '.': + case '/': + indexURL = index; + break; + default: + indexURL = this.getDocumentPath(index); + break; + } + var self = this; + function loadIndex() + { + self.engine.loadIndex$S(window.searchIndex); + self.initialized = true; + window.searchIndex = null; + if (self.reserveSearch) + { + self.search(); + } + self.reserveSearch = false; + } + if (window.searchIndex) + { + loadIndex() + } + else + { + this.loadJavaScript(indexURL, loadIndex); + } + } + + /** + * Changes result page. + * @param {integer} page Page number + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.changePage = function (page) + { + this.engine.setCurrentPage$I(page); + this.updateResult(); + }; + + /** + * Clears a search form and a reult window. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.clearResult = function () + { + $('.oktavia_search', this.node).val(''); + $('.oktavia_searchresult_box', this.node).hide(); + }; + + /** + * Loads an external JavaScript file. + * + * This code is based on: http://os0x.hatenablog.com/entry/20080827/1219815828 + * @param {string} src A JavaScript source file path + * @param {function} callback It is called when the target JavaScript file is loaded + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.loadJavaScript = function (src, callback) + { + var sc = document.createElement('script'); + sc.type = 'text/javascript'; + if (window.ActiveXObject) + { + sc.onreadystatechange = function () + { + if (sc.readyState === 'complete' || sc.readyState === 'loaded') + { + callback(sc.readyState); + } + }; + } + else + { + sc.onload = function () + { + callback('onload'); + }; + } + sc.src = src; + document.body.appendChild(sc); + }; + + /** + * Updates page navigation list. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.updatePageList = function () + { + var self = this; + function createCallback(i) + { + return function () { + self.changePage(i); + }; + } + + var currentPage = String(this.engine.currentPage$()); + var nav = $('.oktavia_searchresult_nav', this.node); + nav.empty(); + var pages = this.engine.pageIndexes$(); + for (var i = 0; i < pages.length; i++) + { + var pageItem = $('').text(pages[i]); + if (pages[i] === '...') + { + pageItem.addClass('leader'); + } + else + { + pageItem.addClass('page'); + if (pages[i] !== currentPage) + { + pageItem.bind('click', createCallback(Number(pages[i]))); + } + else + { + pageItem.addClass('selected'); + } + } + nav.append(pageItem); + } + }; + + /** + * Updates result list in a result window. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.updateResult = function () + { + var totalPages = this.engine.totalPages$(); + var resultslot = $('.oktavia_searchresult', this.node); + resultslot.empty(); + var self = this; + function clearCallback() + { + self.clearResult(); + } + var results = this.engine.getResult$(); + var searchInput = $('.oktavia_search', this.node); + var queryWord = searchInput.val() + for (var i = 0; i < results.length; i++) + { + var result = results[i]; + var url = this.getDocumentPath(result.url.slice(1)) + var entry = $('
', { "class": "entry" }); + var link = $('', { "href": url + this.engine.getHighlight$() }).text(result.title); + link.bind('click', clearCallback); + entry.append($('
', { "class": "title" }).append(link)); + entry.append($('
', { "class": "url" }).text(url)); + entry.append($('
', { "class": "resultcontent" }).html(result.content)); + resultslot.append(entry); + } + this.updatePageList(); + }; + + /** + * Searchs again by using proposal search words. + * @param {string} option Proposal search words + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.searchProposal = function (option) + { + $('.oktavia_search', this.node).val(option); + this.search(); + }; + + /** + * Shows proposals when no result. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.updateProposal = function () + { + var nav = $('.oktavia_searchresult_nav', this.node); + var resultslot = $('.oktavia_searchresult', this.node); + nav.empty(); + resultslot.empty(); + var proposals = this.engine.getProposals$(); + var self = this; + function createCallback(option) + { + return function () + { + self.searchProposal(option); + }; + } + for (var i = 0; i < proposals.length; i++) + { + var proposal = proposals[i]; + var listitem = $('
', {"class": "proposal"}); + listitem.append('Search with: '); + var option = $('', {"class": "option"}); + option.html(proposal.label); + option.bind('click', createCallback(proposal.options)); + listitem.append(option); + listitem.append(' → ' + proposal.count + ' results.'); + resultslot.append(listitem); + } + }; + + /** + * Performs search and shows results. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.search = function () + { + if (!this.initialized) + { + this.reserveSearch = true; + return; + } + var searchInput = $('.oktavia_search', this.node); + var queryWord = searchInput.val(); + searchInput.blur(); + var self = this; + this.engine.search$SF$IIV$(queryWord, function (total, pages) + { + $('.oktavia_searchresult_box', self.node).fadeIn(); + var summaryNode = $('.oktavia_searchresult_summary', self.node); + if (total === 0) + { + summaryNode.text("No result."); + self.updateProposal(); + } + else + { + summaryNode.text(total + ' results.'); + self.updateResult(); + } + }); + }; + + /** + * Converts file path in index. + * @param {string} filePath Source filepath + * @returns {string} Result filepath + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.getDocumentPath = function (filePath) + { + var resultFilePath; + if (filePath.charAt(0) === '/') + { + resultFilePath = filePath; + } + else + { + var elements = filePath.split(/\//g); + var result = this.documentRoot.slice(); + for (var i = 0; i < elements.length; i++) + { + var element = elements[i]; + switch (element) + { + case '.': + break; + case '..': + result = result.slice(0, -1); + break; + default: + result.push(element); + break; + } + } + resultFilePath = result.join('/'); + } + return resultFilePath; + }; + + /** + * Hides all result windows. + * @function + */ + function eraseResultWindow() + { + $('.oktavia_searchresult_box:visible').hide(); + } + + /** + * jQuery plug-in to create search form and window. + * It can receive options from data attributes or an option parameter. + * @param {object} [option] Option + * @param {string} [option.index='search/searchindex.js'] Index file path. + * @param {string} [option.documentRoot='.'] Document root folder. + * @param {string} [option.logo='true'] Show logo in result windows. 'false' or 'disable' or falsy value disable logo. + * @name oktaviaSearch + * @function + */ + jQuery.fn.oktaviaSearch = function (option) + { + var data = { + 'index': 'search/searchindex.js', + 'documentRoot': '.', + 'logo': 'true' + }; + if (window.DOCUMENTATION_OPTIONS) // Sphinx + { + if (window.DOCUMENTATION_OPTIONS.URL_ROOT === '#') + { + data.documentRoot = ''; + } + else + { + data.documentRoot = window.DOCUMENTATION_OPTIONS.URL_ROOT; + } + } + var userData = this.data(); + var key; + for (key in userData) + { + if (userData.hasOwnProperty(key)) + { + data[key] = userData[key]; + } + } + for (key in option) + { + if (option.hasOwnProperty(key)) + { + data[key] = option[key]; + } + } + if (data.logo === 'false' || data.logo === 'disable' || !data.logo) + { + data.logo = false; + } + else + { + data.logo = true; + } + var view = new SearchView(this, data.documentRoot, data.index); + + var form = $('
'); + form.submit(function (event) { + event.stopPropagation(); + setTimeout(function () { + view.search(); + }, 10); + return false; + }); + this.append(form); + var resultForm = $([ + '
', + '', + '
', + '
', + '
', + '
' + ].join('')); + if (data.logo) + { + resultForm.append($('Powered by
Oktavia')); + } + this.append(resultForm); + $('.oktavia_close_search_box', this.node).bind('click', function (event) { + view.clearResult(); + }); + + // Click outside of the result window, close it + resultForm.bind('click', function (event) { + event.stopPropagation(); + }); + }; + + /** + * Global initailization. + * It add some event handlers. + * @name initialize + * @function + */ + function initialize() + { + + function onClick() { + eraseResultWindow(); + return true; + } + function onKeyDown(event) + { + switch (event.keyCode) + { + case 191: // / : focus form + eraseResultWindow(); + var form = $('form.oktavia_form:first input.search'); + if ($(':focus', form).size() === 0) + { + form.focus(); + } + break; + case 74: // j : down + case 75: // k : up + case 72: // h : before page + case 76: // l : next page + case 13: // enter : select + var result = $('.oktavia_searchresult_box:visible:first'); + if (result.size() === 1) + { + switch (event.keyCode) + { + case 74: // j : down + console.log('down'); + break; + case 75: // k : up + console.log('up'); + break; + case 72: // h : before page + console.log('before'); + break; + case 76: // l : next page + console.log('next'); + break; + case 13: // enter : select + console.log('select'); + break; + } + } + break; + } + return true; + } + var version = $.fn.jquery.split(/\./g); + var major = Number(version[0]); + var minor = Number(version[1]); + if (major === 1 && minor < 7) + { + $(document).live('click', onClick); + $(document).live('keydown', onKeyDown); + } + else + { + $(document).on('click', onClick); + $(document).on('keydown', onKeyDown); + } + } + + var logosrc = "data:image/jpeg;base64, /9j/4AAQSkZJRgABAQEASABIAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBwbAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBsAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAAAG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAHMAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFkAAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRHZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJvZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAoAAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUAAAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtvS1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAADHmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAaAAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwAAAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZpRkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAgAFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcAQgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDoAHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8EFwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIARwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkAbMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAgAHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEAbABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBsAHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIARwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBvAGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACADwAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBlAG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBHAGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4ERAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYnBkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwAZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABzAGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdodHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRNAAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8AALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA/ZH///ui///9owAAA9wAAMBs/+EAgEV4aWYAAE1NACoAAAAIAAUBEgADAAAAAQABAAABGgAFAAAAAQAAAEoBGwAFAAAAAQAAAFIBKAADAAAAAQACAACHaQAEAAAAAQAAAFoAAAAAAAAASAAAAAEAAABIAAAAAQACoAIABAAAAAEAAABWoAMABAAAAAEAAAAYAAAAAP/bAEMAAgICAgIBAgICAgICAgMDBgQDAwMDBwUFBAYIBwgICAcICAkKDQsJCQwKCAgLDwsMDQ4ODg4JCxARDw4RDQ4ODv/bAEMBAgICAwMDBgQEBg4JCAkODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODv/AABEIABgAVgMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP3571+dfjn4ofEz4+/tjeJPg38JPE+peAfh/wCE9Q/szxJ4g0namo6xqCoss9tBM6sLe2gRlEkqqXLNtXnFfooa/Lj9jvU7PwH+3T+0D8J/E00Vt4usvHOs3MfmsAbqDUbmO+tp1J6qyN5ZPZkCnqBTQG7pnwW1VP2lde8FfB74qfF3R/E3hrT459f8Val4xutRsIryVd8Nq1ncGRZlYD5zlSoPHTBufD39oH4y+F9H8Xaj8Ur2w8SxeCvES6P4+0tLBIbzSklI8m/t5IwFmt2BBwVB/PI9Z+DOtWfhn/goR+0p8P8AxDcw6f4j1XV7bxLo0dw4U3+nyWwQvHk/MI3RlbH3a+SvG/jnRdbi/bY+JOkyJc+HvF0mm+BvBoiGf+Eh1OGPy3a2H/LUBnHzLkYXNfa0KNGnmlDCxpKVOpGmndXb5knKSb1i7t2cbWtrfW/w+IjiK2WVsRKu41ISm42dlGzajFpWUlZK6le921bS36G+GPHWq61+2r4+8I/2jFdeGdO8N6Xf6fEkKja1wZdz7wNzBgq8E4Fcr8T/ANpDwX4U0bw63hzxb4P1S5vPE8Gm3zveCRLW2WULdzDacHyhwxzhSwJz0Pxp430f4ww/H74qeEvABimudH+FHhxvFdvFcvDqGoW8KSCSztJFB2STBZlL9QFwOWr2L4k+IPh1r/7Fn7MuufDG00638E3XxJ8Pw6dbQQqPsymdlkgcdQ6uCrg8lgc5r1P7EwEcVhqk1zRlyK0bJL92neWm8nqu9m7nl1MzzN4PE0oS5Jpzd5Xb1ntHXaK0v00SXU+s9e+M/wAKfDGg6NqevfEDwrpljq9st1pckt8v+lwtyssYGSyH+8Bj3rQ0v4o/DrW7Hw/daR408OajBrl09ppElveowu50Xc0SY6yBeSvXHavmb/hIr7Uv22vivD8EvhJoXirxTpP2LTPFfibxX4ke0s7aRYQ8VpawiKVgqo4ZtiopJzyea+e7PQfFGteEv2tLyxh8Op8QPAXjrTfE+nW3htX+xLfWtos00UO7DfPGJImJAJYtx2rzcNw5g6tP35ODtF6uL+NpK8VrFJyTu3qleyvp6tfPsxp1PdhGSvJaKS+FNu0no20mrJWTduZ21/Rf4l6/d6N8PWtNC8UeF/C/jLU3Nv4cfXojLb3NyqmTyjGrKzAqrZKnKj5sHGD5j4L8W6fZfC6y+M3xg+IHguN9UQjSxY6iP7G0qFiR5Fs7HNxM2DvmILNgqoVBg+X6B4vsPj9+14vjbQ5xdeB/AXgBbq0kVt0b6tq1uZCPTfDbAA9wZe1eNfCXxXp9x8Ev2UdE8N/DhfiX8Xl8F3uq6PFqet/YdL0qzM5iluZiVcNIWwi7Y2Yc4IzzyUuFaLSlVlLnjbmjdJK8aklrKyWkY3b25no2kj13xhjIUKlCjThySb5Z8rcnZwi0mtWm3L3Vvy7pSlf9C/BnxO+H3xE+2f8ACEeMNB8TPaAG6isboPJCD0LJ94A+uMUV8JaxqPjex/4KWaZ/wmlh4A8NeJrr4bXUslp4Su5pdsI1C1VPtErpGXcndtwgAAPWisswyGlTlB0p6SSfR2+asntvZHDg+Iq/LKNan7ybV1dX+Tu16XZyPjX4k/tYftcfF/xR8Kvgh4b1/wDZ7+EOkanc6R4p8f8AiK2e31O6khkMcsVqqsGUHBwIW3FWy00BG1vWrX/gnT8EtJ+DfhvS/Dmq+NfDPxL0dnmT4m6dfKuvXs8mDI10zKY54jhQIXUqiqoXGM0UV8wfXC6h+xb418baxpK/Fv8Aad+IHjrRtOt5LSOKy8PadpV9PbyAeZDJexRmYI+BuCFc4x0yD6x8Lv2Pfgl8J/GOleINE0rX9e1jSQ40WfxHrM2oJpW8kubWJz5ULMScuqBj60UV00sdiKUXGE2k1Z2fTt6eRhPC0Zu8op9fn39dD2fTfhx4Y0r9oPxP8TrOG9XxXr+mWmnajK92zRNDamQwhYz8qkGR8kcnPPQV5ZqX7Kvwh1L4deMfCbWHiGx0HxF4lTxJLaWOuTwLp+pK277TZ7WH2Zi3JVMKTnjk0UVdLMsXTlzQqNPTq/s/D93TsKeEozVpRT3/AB3+8zT+yX8OIfEY1nSPEvxc8N6xPpdvp+sX2keOL22n1tIF2JJeOrZmn28GbIkOBluK9C+G/wADPhv8Jtc8WXngTR7nR4vEa2/9qWbX0s9vI0KMgkCSFsSOGYyPnMjEsxJ5ooq62bY2rBwnVk09GrvW21/Syt2M45fhozU1BXXWxL8MPgl8Pfg98LtY8H+A9KuNM0TU7+e9vElu3mkaSZQrYdiSFVQqqvRQABXAT/snfCj/AIQX4daNpD+NfC134GspLHw5rmh+JLi01O3tpGLPA86nMsbE5KuGFFFEc1xiqSqKrLmk7t3ers1r30bXo2U8Fh3BQ5FZdLfMS3/ZL+Elp4qtvEUA8aHxWtrPbXmvz+JrqfUNRSWSJ2+0zSMxlwYUCZ4RQVUKCRRRRTnm+Nl8VVv1ZCy3CramvuP/2Q=="; + initialize(); +})(jQuery); + +jQuery(document).ready(function () { + var form = jQuery('#oktavia_search_form'); + if (form.size() > 0) + { + form.oktaviaSearch(); + } +}); diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-search.js b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-search.js new file mode 100644 index 000000000..fce9732a7 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/oktavia-search.js @@ -0,0 +1,6795 @@ +// generatedy by JSX compiler 0.9.24 (2013-04-05 13:45:00 +0900; 1b229cc6a411f674f0f7cf7a79b7a8b3f8eb7414) +var JSX = {}; +(function (JSX) { +/** + * copies the implementations from source interface to target + */ +function $__jsx_merge_interface(target, source) { + for (var k in source.prototype) + if (source.prototype.hasOwnProperty(k)) + target.prototype[k] = source.prototype[k]; +} + +/** + * defers the initialization of the property + */ +function $__jsx_lazy_init(obj, prop, func) { + function reset(obj, prop, value) { + delete obj[prop]; + obj[prop] = value; + return value; + } + + Object.defineProperty(obj, prop, { + get: function () { + return reset(obj, prop, func()); + }, + set: function (v) { + reset(obj, prop, v); + }, + enumerable: true, + configurable: true + }); +} + +/** + * sideeffect().a /= b + */ +function $__jsx_div_assign(obj, prop, divisor) { + return obj[prop] = (obj[prop] / divisor) | 0; +} + +/* + * global functions, renamed to avoid conflict with local variable names + */ +var $__jsx_parseInt = parseInt; +var $__jsx_parseFloat = parseFloat; +var $__jsx_isNaN = isNaN; +var $__jsx_isFinite = isFinite; + +var $__jsx_encodeURIComponent = encodeURIComponent; +var $__jsx_decodeURIComponent = decodeURIComponent; +var $__jsx_encodeURI = encodeURI; +var $__jsx_decodeURI = decodeURI; + +var $__jsx_ObjectToString = Object.prototype.toString; +var $__jsx_ObjectHasOwnProperty = Object.prototype.hasOwnProperty; + +/* + * profiler object, initialized afterwards + */ +function $__jsx_profiler() { +} + +/* + * public interface to JSX code + */ +JSX.require = function (path) { + var m = $__jsx_classMap[path]; + return m !== undefined ? m : null; +}; + +JSX.profilerIsRunning = function () { + return $__jsx_profiler.getResults != null; +}; + +JSX.getProfileResults = function () { + return ($__jsx_profiler.getResults || function () { return {}; })(); +}; + +JSX.postProfileResults = function (url, cb) { + if ($__jsx_profiler.postResults == null) + throw new Error("profiler has not been turned on"); + return $__jsx_profiler.postResults(url, cb); +}; + +JSX.resetProfileResults = function () { + if ($__jsx_profiler.resetResults == null) + throw new Error("profiler has not been turned on"); + return $__jsx_profiler.resetResults(); +}; +JSX.DEBUG = false; +/** + * class _Result extends Object + * @constructor + */ +function _Result() { +} + +/** + * @constructor + * @param {!string} title + * @param {!string} url + * @param {!string} content + * @param {!number} score + */ +function _Result$SSSI(title, url, content, score) { + this.title = title; + this.url = url; + this.content = content; + this.score = score; +}; + +_Result$SSSI.prototype = new _Result; + +/** + * class _Proposal extends Object + * @constructor + */ +function _Proposal() { +} + +/** + * @constructor + * @param {!string} options + * @param {!string} label + * @param {!number} count + */ +function _Proposal$SSI(options, label, count) { + this.options = options; + this.label = label; + this.count = count; +}; + +_Proposal$SSI.prototype = new _Proposal; + +/** + * class OktaviaSearch extends Object + * @constructor + */ +function OktaviaSearch() { +} + +/** + * @constructor + * @param {!number} entriesPerPage + */ +function OktaviaSearch$I(entriesPerPage) { + this._queries = null; + this._highlight = ""; + this._result = null; + this._proposals = null; + this._currentFolderDepth = 0; + this._oktavia = new Oktavia$(); + this._entriesPerPage = entriesPerPage; + this._currentPage = 1; + this._queryString = null; + this._callback = null; + OktaviaSearch._instance = this; +}; + +OktaviaSearch$I.prototype = new OktaviaSearch; + +/** + * @param {Stemmer} stemmer + */ +OktaviaSearch.setStemmer$LStemmer$ = function (stemmer) { + /** @type {Oktavia} */ + var this$0; + if (OktaviaSearch._instance) { + this$0 = OktaviaSearch._instance._oktavia; + this$0._stemmer = stemmer; + } else { + OktaviaSearch._stemmer = stemmer; + } +}; + +var OktaviaSearch$setStemmer$LStemmer$ = OktaviaSearch.setStemmer$LStemmer$; + +/** + * @param {!string} index + */ +OktaviaSearch.prototype.loadIndex$S = function (index) { + /** @type {Oktavia} */ + var this$0; + /** @type {Stemmer} */ + var stemmer$0; + if (OktaviaSearch._stemmer) { + this$0 = this._oktavia; + stemmer$0 = OktaviaSearch._stemmer; + this$0._stemmer = stemmer$0; + } + this._oktavia.load$S(Binary$base64decode$S(index)); + if (this._queryString) { + this.search$SF$IIV$(this._queryString, this._callback); + this._queryString = null; + this._callback = null; + } +}; + +/** + * @param {!string} queryString + * @param {*} callback + */ +OktaviaSearch.prototype.search$SF$IIV$ = function (queryString, callback) { + /** @type {QueryStringParser} */ + var queryParser; + /** @type {SearchSummary} */ + var summary; + /** @type {Array.} */ + var _result$0; + if (this._oktavia) { + queryParser = ({queries: [ ]}); + this._queries = QueryStringParser$parse$LQueryStringParser$S(queryParser, queryString); + this._highlight = QueryStringParser$highlight$LQueryStringParser$(queryParser); + summary = this._oktavia.search$ALQuery$(this._queries); + if (SearchSummary$size$LSearchSummary$(summary) > 0) { + this._result = this._sortResult$LSearchSummary$(summary); + this._proposals = [ ]; + this._currentPage = 1; + } else { + this._result = [ ]; + if (this._queries.length > 1) { + this._proposals = SearchSummary$getProposal$LSearchSummary$(summary); + } else { + this._proposals = [ ]; + } + this._currentPage = 1; + } + callback((_result$0 = this._result).length, Math.ceil(_result$0.length / this._entriesPerPage)); + } else { + this._queryString = queryString; + this._callback = callback; + } +}; + +/** + * @return {!number} + */ +OktaviaSearch.prototype.resultSize$ = function () { + return (this._result.length | 0); +}; + +/** + * @return {!number} + */ +OktaviaSearch.prototype.totalPages$ = function () { + return (Math.ceil(this._result.length / this._entriesPerPage) | 0); +}; + +/** + * @return {!number} + */ +OktaviaSearch.prototype.currentPage$ = function () { + return this._currentPage; +}; + +/** + * @param {!number} page + */ +OktaviaSearch.prototype.setCurrentPage$I = function (page) { + this._currentPage = page; +}; + +/** + * @return {!boolean} + */ +OktaviaSearch.prototype.hasPrevPage$ = function () { + return this._currentPage !== 1; +}; + +/** + * @return {!boolean} + */ +OktaviaSearch.prototype.hasNextPage$ = function () { + return this._currentPage !== Math.ceil(this._result.length / this._entriesPerPage); +}; + +/** + * @return {Array.} + */ +OktaviaSearch.prototype.pageIndexes$ = function () { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var total; + /** @type {!number} */ + var i; + result = [ ]; + total = Math.ceil(this._result.length / this._entriesPerPage); + if (total < 10) { + for (i = 1; i <= total; i++) { + result.push(i + ""); + } + } else { + if (this._currentPage <= 5) { + for (i = 1; i <= 7; i++) { + result.push(i + ""); + } + result.push('...', total + ""); + } else { + if (total - 5 <= this._currentPage) { + result.push('1', '...'); + for (i = total - 8; i <= total; i++) { + result.push(i + ""); + } + } else { + result.push('1', '...'); + for (i = this._currentPage - 3; i <= this._currentPage + 3; i++) { + result.push(i + ""); + } + result.push('...', total + ""); + } + } + } + return result; +}; + +/** + * @return {Array.} + */ +OktaviaSearch.prototype.getResult$ = function () { + /** @type {Style} */ + var style; + /** @type {!number} */ + var start; + /** @type {!number} */ + var last; + /** @type {Metadata} */ + var metadata; + /** @type {!number} */ + var num; + /** @type {Array.} */ + var results; + /** @type {!number} */ + var i; + /** @type {SearchUnit} */ + var unit; + /** @type {Array.} */ + var info; + /** @type {!string} */ + var content; + /** @type {Array.} */ + var positions; + /** @type {!number} */ + var end; + /** @type {!boolean} */ + var split; + /** @type {!number} */ + var j; + /** @type {Position} */ + var pos; + /** @type {!string} */ + var text; + /** @type {Oktavia} */ + var this$0; + /** @type {!number} */ + var position$0; + /** @type {!number} */ + var _currentPage$0; + /** @type {!number} */ + var _entriesPerPage$0; + style = new Style$S('html'); + start = ((_currentPage$0 = this._currentPage) - 1) * (_entriesPerPage$0 = this._entriesPerPage); + last = Math.min(_currentPage$0 * _entriesPerPage$0, this._result.length); + this$0 = this._oktavia; + metadata = this$0._metadatas[this$0._metadataLabels[0]]; + num = 250; + results = [ ]; + for (i = start; i < last; i++) { + unit = this._result[i]; + info = metadata.getInformation$I(unit.id).split(Oktavia.eob); + content = metadata.getContent$I(unit.id); + start = 0; + positions = SearchUnit$getPositions$LSearchUnit$(unit); + if (content.indexOf(info[0]) === 1) { + content = content.slice(info[0].length + 2, content.length); + start += info[0].length + 2; + } + end = start + num; + split = false; + if (positions[0].position > end - positions[0].word.length) { + end = positions[0].position + Math.floor(num / 2); + split = true; + } + for (j = positions.length - 1; j > -1; j--) { + pos = positions[j]; + if (pos.position + pos.word.length < end) { + content = [ content.slice(0, pos.position - start), style.convert$S('*').replace('*', content.slice((position$0 = pos.position) - start, position$0 + pos.word.length - start)), content.slice(pos.position + pos.word.length - start, content.length) ].join(''); + } + } + if (split) { + text = [ content.slice(0, Math.floor(num / 2)) + ' ...', content.slice(- Math.floor(num / 2), end - start) ].join('
'); + } else { + text = content.slice(0, end - start) + ' ...
'; + } + text = text.replace(Oktavia.eob, ' ').replace(/()()+/, '

'); + results.push(({title: info[0], url: info[1], content: text, score: unit.score})); + } + return results; +}; + +/** + * @return {!string} + */ +OktaviaSearch.prototype.getHighlight$ = function () { + return this._highlight; +}; + +/** + * @return {Array.} + */ +OktaviaSearch.prototype.getProposals$ = function () { + /** @type {Style} */ + var style; + /** @type {Array.} */ + var results; + /** @type {!number} */ + var i; + /** @type {Proposal} */ + var proposal; + /** @type {Array.} */ + var label; + /** @type {Array.} */ + var option; + /** @type {!number} */ + var j; + style = new Style$S('html'); + results = [ ]; + if (this._queries.length > 1) { + for (i = 0; i < this._proposals.length; i++) { + proposal = this._proposals[i]; + if (proposal.expect > 0) { + label = [ ]; + option = [ ]; + for (j = 0; j < this._queries.length; j++) { + if (j !== proposal.omit) { + label.push(style.convert$S('' + this._queries[j].toString() + '')); + option.push(this._queries[j].toString()); + } else { + label.push(style.convert$S('' + this._queries[j].toString() + '')); + } + } + results.push(({options: option.join(' '), label: label.join(' '), count: proposal.expect})); + } + } + } + return results; +}; + +/** + * @param {SearchSummary} summary + * @return {Array.} + */ +OktaviaSearch.prototype._sortResult$LSearchSummary$ = function (summary) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var score; + /** @type {SearchUnit} */ + var unit; + /** @type {!string} */ + var pos; + /** @type {Position} */ + var position; + for (i = 0; i < summary.result.units.length; i++) { + score = 0; + unit = summary.result.units[i]; + for (pos in unit.positions) { + position = unit.positions[pos]; + if (this._oktavia.wordPositionType$I(position.position)) { + score += 10; + } else { + score += 1; + } + if (! position.stemmed) { + score += 2; + } + } + unit.score = (score | 0); + } + return SearchSummary$getSortedResult$LSearchSummary$(summary); +}; + +/** + * class _Main extends Object + * @constructor + */ +function _Main() { +} + +/** + * @constructor + */ +function _Main$() { +}; + +_Main$.prototype = new _Main; + +/** + * @param {Array.} args + */ +_Main.main$AS = function (args) { +}; + +var _Main$main$AS = _Main.main$AS; + +/** + * class Oktavia extends Object + * @constructor + */ +function Oktavia() { +} + +/** + * @constructor + */ +function Oktavia$() { + /** @type {Array.} */ + var _utf162compressCode$0; + this._compressCode2utf16 = null; + this._fmindex = new FMIndex$(); + this._metadatas = ({ }); + this._metadataLabels = [ ]; + this._stemmer = null; + this._stemmingResult = ({ }); + _utf162compressCode$0 = this._utf162compressCode = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; + _utf162compressCode$0.length = 65536; + this._compressCode2utf16 = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; +}; + +Oktavia$.prototype = new Oktavia; + +/** + * @param {Stemmer} stemmer + */ +Oktavia.prototype.setStemmer$LStemmer$ = function (stemmer) { + this._stemmer = stemmer; +}; + +/** + * @return {Metadata} + */ +Oktavia.prototype.getPrimaryMetadata$ = function () { + return this._metadatas[this._metadataLabels[0]]; +}; + +/** + * @param {!string} key + * @return {Section} + */ +Oktavia.prototype.addSection$S = function (key) { + /** @type {Section} */ + var section; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + section = new Section$LOktavia$(this); + this._metadatas[key] = section; + return section; +}; + +/** + * @param {!string} key + * @return {Section} + */ +Oktavia.prototype.getSection$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + * @param {!string} key + * @return {Splitter} + */ +Oktavia.prototype.addSplitter$S = function (key) { + /** @type {Splitter} */ + var splitter; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + splitter = new Splitter$LOktavia$(this); + this._metadatas[key] = splitter; + return splitter; +}; + +/** + * @param {!string} key + * @return {Splitter} + */ +Oktavia.prototype.getSplitter$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + * @param {!string} key + * @param {Array.} headers + * @return {Table} + */ +Oktavia.prototype.addTable$SAS = function (key, headers) { + /** @type {Table} */ + var table; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + table = new Table$LOktavia$AS(this, headers); + this._metadatas[key] = table; + return table; +}; + +/** + * @param {!string} key + * @return {Table} + */ +Oktavia.prototype.getTable$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + * @param {!string} key + * @return {Block} + */ +Oktavia.prototype.addBlock$S = function (key) { + /** @type {Block} */ + var block; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + block = new Block$LOktavia$(this); + this._metadatas[key] = block; + return block; +}; + +/** + * @param {!string} key + * @return {Block} + */ +Oktavia.prototype.getBlock$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + */ +Oktavia.prototype.addEndOfBlock$ = function () { + this._fmindex.push$S(Oktavia.eob); +}; + +/** + * @param {!string} words + */ +Oktavia.prototype.addWord$S = function (words) { + /** @type {Array.} */ + var str; + /** @type {!number} */ + var i; + /** @type {!number} */ + var charCode; + /** @type {undefined|!string} */ + var newCharCode; + str = [ ]; + str.length = words.length; + for (i = 0; i < words.length; i++) { + charCode = words.charCodeAt(i); + newCharCode = this._utf162compressCode[charCode]; + if (newCharCode == null) { + newCharCode = String.fromCharCode(this._compressCode2utf16.length); + this._utf162compressCode[charCode] = newCharCode; + this._compressCode2utf16.push(String.fromCharCode(charCode)); + } + str.push(newCharCode); + } + this._fmindex.push$S(str.join('')); +}; + +/** + * @param {!string} words + * @param {!boolean} stemming + */ +Oktavia.prototype.addWord$SB = function (words, stemming) { + /** @type {Array.} */ + var wordList; + /** @type {!number} */ + var i; + /** @type {undefined|!string} */ + var originalWord; + /** @type {!string} */ + var smallWord; + /** @type {undefined|!string} */ + var registerWord; + /** @type {!string} */ + var baseWord; + /** @type {!string} */ + var compressedCodeWord; + /** @type {Array.} */ + var stemmedList; + this.addWord$S(words); + wordList = words.split(/\s+/); + for (i = 0; i < wordList.length; i++) { + originalWord = wordList[i]; + smallWord = originalWord.slice(0, 1).toLowerCase() + originalWord.slice(1); + registerWord = null; + if (stemming && this._stemmer) { + baseWord = this._stemmer.stemWord$S(originalWord.toLowerCase()); + if (originalWord.indexOf(baseWord) === -1) { + registerWord = baseWord; + } + } else { + if (originalWord != smallWord) { + registerWord = smallWord; + } + } + if (registerWord) { + compressedCodeWord = this._convertToCompressionCode$S(originalWord); + stemmedList = this._stemmingResult[registerWord]; + if (! stemmedList) { + stemmedList = [ compressedCodeWord ]; + this._stemmingResult[registerWord] = stemmedList; + } else { + if (stemmedList.indexOf(compressedCodeWord) === -1) { + stemmedList.push(compressedCodeWord); + } + } + } + } +}; + +/** + * @param {!string} keyword + * @return {!string} + */ +Oktavia.prototype._convertToCompressionCode$S = function (keyword) { + /** @type {Array.} */ + var resultChars; + /** @type {!number} */ + var i; + /** @type {undefined|!string} */ + var chr; + resultChars = [ ]; + for (i = 0; i < keyword.length; i++) { + chr = this._utf162compressCode[keyword.charCodeAt(i)]; + if (chr == null) { + resultChars.push(Oktavia.unknown); + } else { + resultChars.push(chr); + } + } + return resultChars.join(''); +}; + +/** + * @param {!string} keyword + * @param {!boolean} stemming + * @return {Array.} + */ +Oktavia.prototype.rawSearch$SB = function (keyword, stemming) { + /** @type {Array.} */ + var result; + /** @type {!string} */ + var baseWord; + /** @type {Array.} */ + var stemmedList; + /** @type {!number} */ + var i; + /** @type {undefined|!string} */ + var word; + if (stemming) { + result = [ ]; + if (this._stemmer) { + baseWord = this._stemmer.stemWord$S(keyword.toLowerCase()); + stemmedList = this._stemmingResult[baseWord]; + if (stemmedList) { + for (i = 0; i < stemmedList.length; i++) { + word = stemmedList[i]; + result = result.concat(this._fmindex.search$S(word)); + } + } + } + } else { + result = this._fmindex.search$S(this._convertToCompressionCode$S(keyword)); + } + return result; +}; + +/** + * @param {Array.} queries + * @return {SearchSummary} + */ +Oktavia.prototype.search$ALQuery$ = function (queries) { + /** @type {SearchSummary} */ + var summary; + /** @type {!number} */ + var i; + /** @type {SingleResult} */ + var result$0; + summary = ({sourceResults: [ ], result: null, oktavia: this}); + for (i = 0; i < queries.length; i++) { + result$0 = this._searchQuery$LQuery$(queries[i]); + summary.sourceResults.push(result$0); + } + summary.result = SearchSummary$mergeResult$LSearchSummary$ALSingleResult$(summary, summary.sourceResults); + return summary; +}; + +/** + * @param {Query} query + * @return {SingleResult} + */ +Oktavia.prototype._searchQuery$LQuery$ = function (query) { + /** @type {SingleResult} */ + var result; + /** @type {Array.} */ + var positions; + result = new SingleResult$SBB(query.word, query.or, query.not); + if (query.raw) { + positions = this.rawSearch$SB(query.word, false); + } else { + positions = this.rawSearch$SB(query.word, false).concat(this.rawSearch$SB(query.word, true)); + } + this._metadatas[this._metadataLabels[0]].grouping$LSingleResult$AISB(result, positions, query.word, ! query.raw); + return result; +}; + +/** + */ +Oktavia.prototype.build$ = function () { + this.build$IB(5, false); +}; + +/** + * @param {!number} cacheDensity + * @param {!boolean} verbose + */ +Oktavia.prototype.build$IB = function (cacheDensity, verbose) { + /** @type {!string} */ + var key; + /** @type {!number} */ + var cacheRange; + /** @type {!number} */ + var maxChar; + for (key in this._metadatas) { + this._metadatas[key]._build$(); + } + cacheRange = Math.round(Math.max(1, 100 / Math.min(100, Math.max(0.01, cacheDensity)))); + maxChar = this._compressCode2utf16.length; + this._fmindex.build$SIIB(Oktavia.eof, maxChar, cacheRange, verbose); +}; + +/** + * @return {!string} + */ +Oktavia.prototype.dump$ = function () { + return this.dump$B(false); +}; + +/** + * @param {!boolean} verbose + * @return {!string} + */ +Oktavia.prototype.dump$B = function (verbose) { + /** @type {!string} */ + var header; + /** @type {!string} */ + var fmdata; + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + /** @type {CompressionReport} */ + var report; + /** @type {undefined|!string} */ + var name; + /** @type {!string} */ + var data; + header = Binary$dumpString$SLCompressionReport$("oktavia-01", null).slice(1); + if (verbose) { + console.log("Source text size: " + (this._fmindex.size$() * 2 + "") + ' bytes'); + } + fmdata = this._fmindex.dump$B(verbose); + result = [ header, fmdata ]; + result.push(Binary$dump16bitNumber$I(this._compressCode2utf16.length)); + for (i = 3; i < this._compressCode2utf16.length; i++) { + result.push(this._compressCode2utf16[i]); + } + if (verbose) { + console.log('Char Code Map: ' + (this._compressCode2utf16.length * 2 - 2 + "") + ' bytes'); + } + report = ({source: 0, result: 0}); + result.push(Binary$dumpStringListMap$HASLCompressionReport$(this._stemmingResult, report)); + if (verbose) { + console.log('Stemmed Word Table: ' + (result[result.length - 1].length + "") + ' bytes (' + (Math.round(report.result * 100.0 / report.source) + "") + '%)'); + } + result.push(Binary$dump16bitNumber$I(this._metadataLabels.length)); + for (i = 0; i < this._metadataLabels.length; i++) { + report = ({source: 0, result: 0}); + name = this._metadataLabels[i]; + data = this._metadatas[name]._dump$LCompressionReport$(report); + result.push(Binary$dumpString$SLCompressionReport$(name, report), data); + if (verbose) { + console.log('Meta Data ' + name + ': ' + (data.length * 2 + "") + ' bytes (' + (Math.round(report.result * 100.0 / report.source) + "") + '%)'); + } + } + return result.join(''); +}; + +/** + * @param {!string} data + */ +Oktavia.prototype.load$S = function (data) { + /** @type {!string} */ + var header; + /** @type {!number} */ + var offset; + /** @type {!number} */ + var charCodeCount; + /** @type {!number} */ + var i; + /** @type {!number} */ + var charCode; + /** @type {LoadedStringListMapResult} */ + var stemmedWords; + /** @type {!number} */ + var metadataCount; + /** @type {LoadedStringResult} */ + var nameResult; + /** @type {!string} */ + var name; + /** @type {!number} */ + var type; + header = Binary$dumpString$SLCompressionReport$("oktavia-01", null).slice(1); + if (data.slice(0, 5) !== header) { + throw new Error('Invalid data file'); + } + this._metadatas = ({ }); + this._metadataLabels = [ ]; + offset = 5; + offset = this._fmindex.load$SI(data, offset); + charCodeCount = Binary$load16bitNumber$SI(data, offset++); + this._compressCode2utf16 = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; + this._utf162compressCode = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; + for (i = 3; i < charCodeCount; i++) { + charCode = Binary$load16bitNumber$SI(data, offset++); + this._compressCode2utf16.push(String.fromCharCode(charCode)); + this._utf162compressCode[charCode] = String.fromCharCode(i); + } + stemmedWords = Binary$loadStringListMap$SI(data, offset); + this._stemmingResult = stemmedWords.result; + offset = stemmedWords.offset; + metadataCount = Binary$load16bitNumber$SI(data, offset++); + for (i = 0; i < metadataCount; i++) { + nameResult = Binary$loadString$SI(data, offset); + name = nameResult.result; + offset = nameResult.offset; + type = Binary$load16bitNumber$SI(data, offset++); + switch (type) { + case 0: + offset = Section$_load$LOktavia$SSI(this, name, data, offset); + break; + case 1: + offset = Splitter$_load$LOktavia$SSI(this, name, data, offset); + break; + case 2: + offset = Table$_load$LOktavia$SSI(this, name, data, offset); + break; + case 3: + offset = Block$_load$LOktavia$SSI(this, name, data, offset); + break; + } + } +}; + +/** + * @return {!number} + */ +Oktavia.prototype.contentSize$ = function () { + /** @type {FMIndex} */ + var this$0; + this$0 = this._fmindex; + return this$0._substr.length; +}; + +/** + * @param {!number} position + * @return {!number} + */ +Oktavia.prototype.wordPositionType$I = function (position) { + /** @type {!number} */ + var result; + /** @type {!string} */ + var ahead; + result = 0; + if (position === 0) { + result = 4; + } else { + ahead = this._fmindex.getSubstring$II(position - 1, 1); + if (/\s/.test(ahead)) { + result = 2; + } else { + if (/\W/.test(ahead)) { + result = 1; + } else { + if (Oktavia.eob === ahead) { + result = 3; + } + } + } + } + return (result | 0); +}; + +/** + * @param {!number} position + * @param {!number} length + * @return {!string} + */ +Oktavia.prototype._getSubstring$II = function (position, length) { + /** @type {!string} */ + var result; + /** @type {Array.} */ + var str; + /** @type {!number} */ + var i; + result = this._fmindex.getSubstring$II(position, length); + str = [ ]; + for (i = 0; i < result.length; i++) { + str.push(this._compressCode2utf16[result.charCodeAt(i)]); + } + return str.join(''); +}; + +/** + * class Binary extends Object + * @constructor + */ +function Binary() { +} + +/** + * @constructor + */ +function Binary$() { +}; + +Binary$.prototype = new Binary; + +/** + * @param {!number} num + * @return {!string} + */ +Binary.dump32bitNumber$N = function (num) { + /** @type {Array.} */ + var result; + result = [ String.fromCharCode(Math.floor(num / 65536)) ]; + result.push(String.fromCharCode(num % 65536)); + return result.join(""); +}; + +var Binary$dump32bitNumber$N = Binary.dump32bitNumber$N; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {!number} + */ +Binary.load32bitNumber$SI = function (buffer, offset) { + /** @type {!number} */ + var result; + result = buffer.charCodeAt(offset) * 65536 + buffer.charCodeAt(offset + 1); + return result; +}; + +var Binary$load32bitNumber$SI = Binary.load32bitNumber$SI; + +/** + * @param {!number} num + * @return {!string} + */ +Binary.dump16bitNumber$I = function (num) { + return String.fromCharCode(num % 65536); +}; + +var Binary$dump16bitNumber$I = Binary.dump16bitNumber$I; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {!number} + */ +Binary.load16bitNumber$SI = function (buffer, offset) { + return (buffer.charCodeAt(offset) | 0); +}; + +var Binary$load16bitNumber$SI = Binary.load16bitNumber$SI; + +/** + * @param {!string} str + * @return {!string} + */ +Binary.dumpString$S = function (str) { + return Binary$dumpString$SLCompressionReport$(str, null); +}; + +var Binary$dumpString$S = Binary.dumpString$S; + +/** + * @param {!string} str + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dumpString$SLCompressionReport$ = function (str, report) { + /** @type {!number} */ + var length; + /** @type {!boolean} */ + var compress; + /** @type {Array.} */ + var charCodes; + /** @type {!number} */ + var i; + /** @type {!number} */ + var charCode; + /** @type {Array.} */ + var result; + /** @type {undefined|!number} */ + var bytes; + if (str.length > 32768) { + str = str.slice(0, 32768); + } + length = str.length; + compress = true; + charCodes = [ ]; + for (i = 0; i < length; i++) { + charCode = str.charCodeAt(i); + if (charCode > 255) { + compress = false; + break; + } + charCodes.push(charCode); + } + if (compress) { + result = [ Binary$dump16bitNumber$I(length + 32768) ]; + for (i = 0; i < length; i += 2) { + bytes = charCodes[i]; + if (i !== length - 1) { + bytes += charCodes[i + 1] << 8; + } + result.push(String.fromCharCode(bytes % 65536)); + } + if (report) { + CompressionReport$add$LCompressionReport$II(report, length, Math.ceil(length / 2)); + } + } else { + result = [ Binary$dump16bitNumber$I(length), str ]; + if (report) { + CompressionReport$add$LCompressionReport$II(report, length, length); + } + } + return result.join(''); +}; + +var Binary$dumpString$SLCompressionReport$ = Binary.dumpString$SLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedStringResult} + */ +Binary.loadString$SI = function (buffer, offset) { + return new LoadedStringResult$SI(buffer, offset); +}; + +var Binary$loadString$SI = Binary.loadString$SI; + +/** + * @param {Array.} strList + * @return {!string} + */ +Binary.dumpStringList$AS = function (strList) { + return Binary$dumpStringList$ASLCompressionReport$(strList, null); +}; + +var Binary$dumpStringList$AS = Binary.dumpStringList$AS; + +/** + * @param {Array.} strList + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dumpStringList$ASLCompressionReport$ = function (strList, report) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + result = [ Binary$dump32bitNumber$N(strList.length) ]; + for (i = 0; i < strList.length; i++) { + result.push(Binary$dumpString$SLCompressionReport$(strList[i], report)); + } + return result.join(''); +}; + +var Binary$dumpStringList$ASLCompressionReport$ = Binary.dumpStringList$ASLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedStringListResult} + */ +Binary.loadStringList$SI = function (buffer, offset) { + return new LoadedStringListResult$SI(buffer, offset); +}; + +var Binary$loadStringList$SI = Binary.loadStringList$SI; + +/** + * @param {Object.>} strMap + * @return {!string} + */ +Binary.dumpStringListMap$HAS = function (strMap) { + return Binary$dumpStringListMap$HASLCompressionReport$(strMap, null); +}; + +var Binary$dumpStringListMap$HAS = Binary.dumpStringListMap$HAS; + +/** + * @param {Object.>} strMap + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dumpStringListMap$HASLCompressionReport$ = function (strMap, report) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var counter; + /** @type {!string} */ + var key; + result = [ ]; + counter = 0; + for (key in strMap) { + result.push(Binary$dumpString$SLCompressionReport$(key, report)); + result.push(Binary$dumpStringList$ASLCompressionReport$(strMap[key], report)); + counter++; + } + return Binary$dump32bitNumber$N(counter) + result.join(''); +}; + +var Binary$dumpStringListMap$HASLCompressionReport$ = Binary.dumpStringListMap$HASLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedStringListMapResult} + */ +Binary.loadStringListMap$SI = function (buffer, offset) { + return new LoadedStringListMapResult$SI(buffer, offset); +}; + +var Binary$loadStringListMap$SI = Binary.loadStringListMap$SI; + +/** + * @param {Array.} array + * @return {!string} + */ +Binary.dump32bitNumberList$AN = function (array) { + return Binary$dump32bitNumberList$ANLCompressionReport$(array, null); +}; + +var Binary$dump32bitNumberList$AN = Binary.dump32bitNumberList$AN; + +/** + * @param {Array.} array + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dump32bitNumberList$ANLCompressionReport$ = function (array, report) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var index; + /** @type {!number} */ + var inputLength; + /** @type {!number} */ + var length; + /** @type {!string} */ + var resultString; + /** @type {!number} */ + var value1$0; + /** @type {!number} */ + var value2$0; + result = [ Binary$dump32bitNumber$N(array.length) ]; + index = 0; + inputLength = array.length; + while (index < inputLength) { + if (array[index] == 0) { + length = Binary$_countZero$ANI(array, index); + result.push(Binary$_zeroBlock$I(length)); + index += length; + } else { + if (Binary$_shouldZebraCode$ANI(array, index)) { + result.push(Binary$_createZebraCode$ANI(array, index)); + value1$0 = array.length; + value2$0 = index + 15; + index = (value1$0 <= value2$0 ? value1$0 : value2$0); + } else { + length = Binary$_searchDoubleZero$ANI(array, index); + result.push(Binary$_nonZeroBlock$ANII(array, index, length)); + if (length === 0) { + throw new Error(''); + } + index += length; + } + } + } + resultString = result.join(''); + if (report) { + CompressionReport$add$LCompressionReport$II(report, array.length * 2 + 2, resultString.length); + } + return resultString; +}; + +var Binary$dump32bitNumberList$ANLCompressionReport$ = Binary.dump32bitNumberList$ANLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedNumberListResult} + */ +Binary.load32bitNumberList$SI = function (buffer, offset) { + return new LoadedNumberListResult$SI(buffer, offset); +}; + +var Binary$load32bitNumberList$SI = Binary.load32bitNumberList$SI; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!number} + */ +Binary._countZero$ANI = function (array, offset) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var array$len$0; + for ((i = offset, array$len$0 = array.length); i < array$len$0; i++) { + if (array[i] != 0) { + return (i - offset | 0); + } + } + return (array.length - offset | 0); +}; + +var Binary$_countZero$ANI = Binary._countZero$ANI; + +/** + * @param {!number} length + * @return {!string} + */ +Binary._zeroBlock$I = function (length) { + /** @type {Array.} */ + var result; + result = [ ]; + while (length > 0) { + if (length > 16384) { + result.push(Binary$dump16bitNumber$I(16383)); + length -= 16384; + } else { + result.push(Binary$dump16bitNumber$I(length - 1)); + length = 0; + } + } + return result.join(''); +}; + +var Binary$_zeroBlock$I = Binary._zeroBlock$I; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!boolean} + */ +Binary._shouldZebraCode$ANI = function (array, offset) { + /** @type {!number} */ + var change; + /** @type {!boolean} */ + var isLastZero; + /** @type {!number} */ + var i; + if (array.length - offset < 16) { + return true; + } + change = 0; + isLastZero = false; + for (i = offset; i < offset + 15; i++) { + if (array[i] == 0) { + if (! isLastZero) { + isLastZero = true; + change++; + } + } else { + if (isLastZero) { + isLastZero = false; + change++; + } + } + } + return change > 2; +}; + +var Binary$_shouldZebraCode$ANI = Binary._shouldZebraCode$ANI; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!number} + */ +Binary._searchDoubleZero$ANI = function (array, offset) { + /** @type {!boolean} */ + var isLastZero; + /** @type {!number} */ + var i; + /** @type {!number} */ + var array$len$0; + isLastZero = false; + for ((i = offset, array$len$0 = array.length); i < array$len$0; i++) { + if (array[i] == 0) { + if (isLastZero) { + return (i - offset - 1 | 0); + } + isLastZero = true; + } else { + isLastZero = false; + } + } + return (array.length - offset | 0); +}; + +var Binary$_searchDoubleZero$ANI = Binary._searchDoubleZero$ANI; + +/** + * @param {Array.} array + * @param {!number} offset + * @param {!number} length + * @return {!string} + */ +Binary._nonZeroBlock$ANII = function (array, offset, length) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var blockLength; + /** @type {!number} */ + var i; + result = [ ]; + while (length > 0) { + if (length > 16384) { + blockLength = 16384; + length -= 16384; + } else { + blockLength = length; + length = 0; + } + result.push(Binary$dump16bitNumber$I(blockLength - 1 + 0x4000)); + for (i = offset; i < offset + blockLength; i++) { + result.push(Binary$dump32bitNumber$N(array[i])); + } + offset += blockLength; + } + return result.join(''); +}; + +var Binary$_nonZeroBlock$ANII = Binary._nonZeroBlock$ANII; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!string} + */ +Binary._createZebraCode$ANI = function (array, offset) { + /** @type {!number} */ + var last; + /** @type {!number} */ + var code; + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + /** @type {!number} */ + var value1$0; + /** @type {!number} */ + var value2$0; + value1$0 = offset + 15; + value2$0 = array.length; + last = (value1$0 <= value2$0 ? value1$0 : value2$0); + code = 0x8000; + result = [ ]; + for (i = offset; i < last; i++) { + if (array[i] != 0) { + result.push(Binary$dump32bitNumber$N(array[i])); + code = code + (0x1 << i - offset); + } + } + return String.fromCharCode(code) + result.join(''); +}; + +var Binary$_createZebraCode$ANI = Binary._createZebraCode$ANI; + +/** + * @param {!string} str + * @return {!string} + */ +Binary.base64encode$S = function (str) { + /** @type {Array.} */ + var out; + /** @type {Array.} */ + var source; + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {!number} */ + var len; + /** @type {!number} */ + var c1; + /** @type {undefined|!number} */ + var c2; + /** @type {undefined|!number} */ + var c3; + out = [ ]; + source = [ ]; + for (i = 0; i < str.length; i++) { + code = str.charCodeAt(i); + source.push(code & 0x00ff, code >>> 8); + } + len = str.length * 2; + i = 0; + while (i < len) { + c1 = source[i++] & 0xff; + if (i === len) { + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c1 >> 2)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c1 & 0x3) << 4)); + out.push("=="); + break; + } + c2 = source[i++]; + if (i === len) { + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c1 >> 2)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c1 & 0x3) << 4 | (c2 & 0xF0) >> 4)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c2 & 0xF) << 2)); + out.push("="); + break; + } + c3 = source[i++]; + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c1 >> 2)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c1 & 0x3) << 4 | (c2 & 0xF0) >> 4)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c2 & 0xF) << 2 | (c3 & 0xC0) >> 6)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c3 & 0x3F)); + } + return out.join(''); +}; + +var Binary$base64encode$S = Binary.base64encode$S; + +/** + * @param {Array.} source + * @return {!string} + */ +Binary._mergeCharCode$AI = function (source) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + result = [ ]; + for (i = 0; i < source.length; i += 2) { + result.push(String.fromCharCode(source[i] + (source[i + 1] << 8))); + } + return result.join(''); +}; + +var Binary$_mergeCharCode$AI = Binary._mergeCharCode$AI; + +/** + * @param {!string} str + * @return {!string} + */ +Binary.base64decode$S = function (str) { + /** @type {!number} */ + var len; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var out; + /** @type {undefined|!number} */ + var c1; + /** @type {undefined|!number} */ + var c2; + /** @type {!number} */ + var c3; + /** @type {!number} */ + var c4; + len = str.length; + i = 0; + out = [ ]; + while (i < len) { + do { + c1 = Binary._base64DecodeChars[str.charCodeAt(i++) & 0xff]; + } while (i < len && c1 == -1); + if (c1 == -1) { + break; + } + do { + c2 = Binary._base64DecodeChars[str.charCodeAt(i++) & 0xff]; + } while (i < len && c2 == -1); + if (c2 == -1) { + break; + } + out.push(c1 << 2 | (c2 & 0x30) >> 4); + do { + c3 = str.charCodeAt(i++) & 0xff; + if (c3 === 61) { + return Binary$_mergeCharCode$AI(out); + } + c3 = Binary._base64DecodeChars[c3]; + } while (i < len && c3 === -1); + if (c3 === -1) { + break; + } + out.push((c2 & 0XF) << 4 | (c3 & 0x3C) >> 2); + do { + c4 = str.charCodeAt(i++) & 0xff; + if (c4 === 61) { + return Binary$_mergeCharCode$AI(out); + } + c4 = (Binary._base64DecodeChars[c4] | 0); + } while (i < len && c4 === -1); + if (c4 === -1) { + break; + } + out.push((c3 & 0x03) << 6 | c4); + } + return Binary$_mergeCharCode$AI(out); +}; + +var Binary$base64decode$S = Binary.base64decode$S; + +/** + * class LoadedStringResult extends Object + * @constructor + */ +function LoadedStringResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedStringResult$SI(data, offset) { + /** @type {!number} */ + var strLength; + /** @type {Array.} */ + var bytes; + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {!number} */ + var offset$0; + this.result = ""; + this.offset = 0; + offset$0 = offset++; + strLength = data.charCodeAt(offset$0); + if (strLength > 32767) { + strLength = strLength - 32768; + bytes = [ ]; + for (i = 0; i < strLength; i += 2) { + code = data.charCodeAt(offset); + bytes.push(String.fromCharCode(code & 0x00ff)); + if (i !== strLength - 1) { + bytes.push(String.fromCharCode(code >>> 8)); + } + offset++; + } + this.result = bytes.join(''); + this.offset = offset; + } else { + this.result = data.slice(offset, offset + strLength); + this.offset = (offset + strLength | 0); + } +}; + +LoadedStringResult$SI.prototype = new LoadedStringResult; + +/** + * class LoadedStringListResult extends Object + * @constructor + */ +function LoadedStringListResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedStringListResult$SI(data, offset) { + /** @type {!number} */ + var length; + /** @type {!number} */ + var i; + /** @type {!number} */ + var strLength; + /** @type {!string} */ + var resultStr; + /** @type {Array.} */ + var bytes; + /** @type {!number} */ + var j; + /** @type {!number} */ + var code; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var offset$0; + this.offset = 0; + this.result = [ ]; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + length = result$0; + offset += 2; + for (i = 0; i < length; i++) { + offset$0 = offset++; + strLength = data.charCodeAt(offset$0); + if (strLength > 32767) { + strLength = strLength - 32768; + bytes = [ ]; + for (j = 0; j < strLength; j += 2) { + code = data.charCodeAt(offset); + bytes.push(String.fromCharCode(code & 0x00ff)); + if (j !== strLength - 1) { + bytes.push(String.fromCharCode(code >>> 8)); + } + offset++; + } + resultStr = bytes.join(''); + } else { + resultStr = data.slice(offset, offset + strLength); + offset = (offset + strLength | 0); + } + this.result.push(resultStr); + } + this.offset = offset; +}; + +LoadedStringListResult$SI.prototype = new LoadedStringListResult; + +/** + * class LoadedStringListMapResult extends Object + * @constructor + */ +function LoadedStringListMapResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedStringListMapResult$SI(data, offset) { + /** @type {!number} */ + var length; + /** @type {!number} */ + var i; + /** @type {LoadedStringResult} */ + var keyResult; + /** @type {LoadedStringListResult} */ + var valueResult; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var offset$0; + this.offset = 0; + this.result = ({ }); + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + length = result$0; + offset += 2; + for (i = 0; i < length; i++) { + keyResult = new LoadedStringResult$SI(data, offset); + offset$0 = keyResult.offset; + valueResult = new LoadedStringListResult$SI(data, offset$0); + this.result[keyResult.result] = valueResult.result; + offset = valueResult.offset; + } + this.offset = offset; +}; + +LoadedStringListMapResult$SI.prototype = new LoadedStringListMapResult; + +/** + * class LoadedNumberListResult extends Object + * @constructor + */ +function LoadedNumberListResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedNumberListResult$SI(data, offset) { + /** @type {!number} */ + var resultLength; + /** @type {!number} */ + var originalOffset; + /** @type {Array.} */ + var result; + /** @type {!number} */ + var tag; + /** @type {!number} */ + var length; + /** @type {!number} */ + var i; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var value1$0; + this.result = null; + this.offset = 0; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + resultLength = result$0; + originalOffset = offset; + offset += 2; + result = [ ]; + while (result.length < resultLength) { + tag = data.charCodeAt(offset++); + if (tag >>> 15 === 1) { + value1$0 = resultLength - result.length; + length = (value1$0 <= 15 ? value1$0 : 15); + for (i = 0; i < length; i++) { + if (tag >>> i & 0x1) { + result.push(Binary$load32bitNumber$SI(data, offset)); + offset += 2; + } else { + result.push(0); + } + } + } else { + if (tag >>> 14 === 1) { + length = tag - 0x4000 + 1; + for (i = 0; i < length; i++) { + result.push(Binary$load32bitNumber$SI(data, offset)); + offset += 2; + } + } else { + length = tag + 1; + for (i = 0; i < length; i++) { + result.push(0); + } + } + } + } + this.result = result; + this.offset = offset; +}; + +LoadedNumberListResult$SI.prototype = new LoadedNumberListResult; + +/** + * class CompressionReport extends Object + * @constructor + */ +function CompressionReport() { +} + +/** + * @constructor + */ +function CompressionReport$() { + this.source = 0; + this.result = 0; +}; + +CompressionReport$.prototype = new CompressionReport; + +/** + * @param {CompressionReport} $this + * @param {!number} source + * @param {!number} result + */ +CompressionReport.add$LCompressionReport$II = function ($this, source, result) { + $this.source += source; + $this.result += result; +}; + +var CompressionReport$add$LCompressionReport$II = CompressionReport.add$LCompressionReport$II; + +/** + * @param {CompressionReport} $this + * @return {!number} + */ +CompressionReport.rate$LCompressionReport$ = function ($this) { + return (Math.round($this.result * 100.0 / $this.source) | 0); +}; + +var CompressionReport$rate$LCompressionReport$ = CompressionReport.rate$LCompressionReport$; + +/** + * class Query extends Object + * @constructor + */ +function Query() { +} + +/** + * @constructor + */ +function Query$() { + this.word = ''; + this.or = false; + this.not = false; + this.raw = false; +}; + +Query$.prototype = new Query; + +/** + * @return {!string} + */ +Query.prototype.toString = function () { + /** @type {Array.} */ + var result; + result = [ ]; + if (this.or) { + result.push("OR "); + } + if (this.not) { + result.push("-"); + } + if (this.raw) { + result.push('"', this.word, '"'); + } else { + result.push(this.word); + } + return result.join(''); +}; + +/** + * class QueryStringParser extends Object + * @constructor + */ +function QueryStringParser() { +} + +/** + * @constructor + */ +function QueryStringParser$() { + this.queries = [ ]; +}; + +QueryStringParser$.prototype = new QueryStringParser; + +/** + * @param {QueryStringParser} $this + * @param {!string} queryString + * @return {Array.} + */ +QueryStringParser.parse$LQueryStringParser$S = function ($this, queryString) { + /** @type {!boolean} */ + var nextOr; + /** @type {!boolean} */ + var nextNot; + /** @type {!number} */ + var currentWordStart; + /** @type {!number} */ + var status; + /** @type {RegExp} */ + var isSpace; + /** @type {!number} */ + var i; + /** @type {!string} */ + var ch; + /** @type {!string} */ + var word; + /** @type {Query} */ + var query; + nextOr = false; + nextNot = false; + currentWordStart = 0; + status = 0; + isSpace = /[\s\u3000]/; + for (i = 0; i < queryString.length; i++) { + ch = queryString.charAt(i); + switch (status) { + case 0: + if (! isSpace.test(ch)) { + if (ch === '-') { + nextNot = true; + } else { + if (ch === '"') { + currentWordStart = i + 1; + status = 2; + } else { + currentWordStart = i; + status = 1; + } + } + } else { + nextNot = false; + } + break; + case 1: + if (isSpace.test(ch)) { + word = queryString.slice(currentWordStart, i); + if (word === 'OR') { + nextOr = true; + } else { + query = new Query$(); + query.word = word; + query.or = nextOr; + query.not = nextNot; + $this.queries.push(query); + nextOr = false; + nextNot = false; + } + status = 0; + } + break; + case 2: + if (ch === '"') { + word = queryString.slice(currentWordStart, i); + query = new Query$(); + query.word = word; + query.or = nextOr; + query.not = nextNot; + query.raw = true; + $this.queries.push(query); + nextOr = false; + nextNot = false; + status = 0; + } + break; + } + } + switch (status) { + case 0: + break; + case 1: + query = new Query$(); + word = queryString.slice(currentWordStart, queryString.length); + if (word !== 'OR') { + query.word = word; + query.or = nextOr; + query.not = nextNot; + $this.queries.push(query); + } + break; + case 2: + query = new Query$(); + query.word = queryString.slice(currentWordStart, queryString.length); + query.or = nextOr; + query.not = nextNot; + query.raw = true; + $this.queries.push(query); + break; + } + return $this.queries; +}; + +var QueryStringParser$parse$LQueryStringParser$S = QueryStringParser.parse$LQueryStringParser$S; + +/** + * @param {QueryStringParser} $this + * @return {!string} + */ +QueryStringParser.highlight$LQueryStringParser$ = function ($this) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + /** @type {Query} */ + var query; + result = [ ]; + for (i = 0; i < $this.queries.length; i++) { + query = $this.queries[i]; + if (! query.not) { + result.push("highlight=" + $__jsx_encodeURIComponent(query.word)); + } + } + return '?' + result.join('&'); +}; + +var QueryStringParser$highlight$LQueryStringParser$ = QueryStringParser.highlight$LQueryStringParser$; + +/** + * class Proposal extends Object + * @constructor + */ +function Proposal() { +} + +/** + * @constructor + * @param {!number} omit + * @param {!number} expect + */ +function Proposal$II(omit, expect) { + this.omit = omit; + this.expect = expect; +}; + +Proposal$II.prototype = new Proposal; + +/** + * class Position extends Object + * @constructor + */ +function Position() { +} + +/** + * @constructor + * @param {!string} word + * @param {!number} position + * @param {!boolean} stemmed + */ +function Position$SIB(word, position, stemmed) { + this.word = word; + this.position = position; + this.stemmed = stemmed; +}; + +Position$SIB.prototype = new Position; + +/** + * class SearchUnit extends Object + * @constructor + */ +function SearchUnit() { +} + +/** + * @constructor + * @param {!number} id + */ +function SearchUnit$I(id) { + this.positions = ({ }); + this.id = id; + this._size = 0; + this.score = 0; + this.startPosition = -1; +}; + +SearchUnit$I.prototype = new SearchUnit; + +/** + * @param {SearchUnit} $this + * @param {!string} word + * @param {!number} position + * @param {!boolean} stemmed + */ +SearchUnit.addPosition$LSearchUnit$SIB = function ($this, word, position, stemmed) { + /** @type {Position} */ + var positionObj; + positionObj = $this.positions[position + ""]; + if (! positionObj) { + $this._size++; + $this.positions[position + ""] = ({word: word, position: position, stemmed: stemmed}); + } else { + if (positionObj.word.length < word.length) { + positionObj.word = word; + } + positionObj.stemmed = positionObj.stemmed && stemmed; + } +}; + +var SearchUnit$addPosition$LSearchUnit$SIB = SearchUnit.addPosition$LSearchUnit$SIB; + +/** + * @param {SearchUnit} $this + * @param {!number} position + * @return {Position} + */ +SearchUnit.get$LSearchUnit$I = function ($this, position) { + return $this.positions[position + ""]; +}; + +var SearchUnit$get$LSearchUnit$I = SearchUnit.get$LSearchUnit$I; + +/** + * @param {SearchUnit} $this + * @return {!number} + */ +SearchUnit.size$LSearchUnit$ = function ($this) { + return $this._size; +}; + +var SearchUnit$size$LSearchUnit$ = SearchUnit.size$LSearchUnit$; + +/** + * @param {SearchUnit} $this + * @param {SearchUnit} rhs + */ +SearchUnit.merge$LSearchUnit$LSearchUnit$ = function ($this, rhs) { + /** @type {!string} */ + var position; + /** @type {Position} */ + var pos; + for (position in rhs.positions) { + pos = rhs.positions[position]; + SearchUnit$addPosition$LSearchUnit$SIB($this, pos.word, pos.position, pos.stemmed); + } +}; + +var SearchUnit$merge$LSearchUnit$LSearchUnit$ = SearchUnit.merge$LSearchUnit$LSearchUnit$; + +/** + * @param {SearchUnit} $this + * @return {Array.} + */ +SearchUnit.getPositions$LSearchUnit$ = function ($this) { + /** @type {Array.} */ + var result; + /** @type {!string} */ + var pos; + result = [ ]; + for (pos in $this.positions) { + result.push($this.positions[pos]); + } + result.sort((function (a, b) { + return a.position - b.position; + })); + return result; +}; + +var SearchUnit$getPositions$LSearchUnit$ = SearchUnit.getPositions$LSearchUnit$; + +/** + * class SingleResult extends Object + * @constructor + */ +function SingleResult() { +} + +/** + * @constructor + */ +function SingleResult$() { + this.units = [ ]; + this.unitIds = [ ]; + this.or = false; + this.not = false; + this.searchWord = ''; +}; + +SingleResult$.prototype = new SingleResult; + +/** + * @constructor + * @param {!string} searchWord + * @param {!boolean} or + * @param {!boolean} not + */ +function SingleResult$SBB(searchWord, or, not) { + this.units = [ ]; + this.unitIds = [ ]; + this.or = or; + this.not = not; + this.searchWord = searchWord; +}; + +SingleResult$SBB.prototype = new SingleResult; + +/** + * @param {SingleResult} $this + * @param {!number} unitId + * @return {SearchUnit} + */ +SingleResult.getSearchUnit$LSingleResult$I = function ($this, unitId) { + /** @type {!number} */ + var existing; + /** @type {SearchUnit} */ + var result; + existing = $this.unitIds.indexOf(unitId); + if (existing === -1) { + result = ({positions: ({ }), id: unitId, _size: 0, score: 0, startPosition: -1}); + $this.units.push(result); + $this.unitIds.push(unitId); + } else { + result = $this.units[existing]; + } + return result; +}; + +var SingleResult$getSearchUnit$LSingleResult$I = SingleResult.getSearchUnit$LSingleResult$I; + +/** + * @param {SingleResult} $this + * @param {SingleResult} rhs + * @return {SingleResult} + */ +SingleResult.merge$LSingleResult$LSingleResult$ = function ($this, rhs) { + /** @type {SingleResult} */ + var result; + result = ({units: [ ], unitIds: [ ], or: false, not: false, searchWord: ''}); + if (rhs.or) { + SingleResult$_orMerge$LSingleResult$LSingleResult$LSingleResult$($this, result, rhs); + } else { + if (rhs.not) { + SingleResult$_notMerge$LSingleResult$LSingleResult$LSingleResult$($this, result, rhs); + } else { + SingleResult$_andMerge$LSingleResult$LSingleResult$LSingleResult$($this, result, rhs); + } + } + return result; +}; + +var SingleResult$merge$LSingleResult$LSingleResult$ = SingleResult.merge$LSingleResult$LSingleResult$; + +/** + * @param {SingleResult} $this + * @return {!number} + */ +SingleResult.size$LSingleResult$ = function ($this) { + return ($this.units.length | 0); +}; + +var SingleResult$size$LSingleResult$ = SingleResult.size$LSingleResult$; + +/** + * @param {SingleResult} $this + * @param {SingleResult} result + * @param {SingleResult} rhs + */ +SingleResult._andMerge$LSingleResult$LSingleResult$LSingleResult$ = function ($this, result, rhs) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var id; + /** @type {SearchUnit} */ + var lhsSection; + for (i = 0; i < $this.unitIds.length; i++) { + id = $this.unitIds[i]; + if (rhs.unitIds.indexOf(id) !== -1) { + lhsSection = $this.units[i]; + result.unitIds.push(id); + result.units.push(lhsSection); + } + } +}; + +var SingleResult$_andMerge$LSingleResult$LSingleResult$LSingleResult$ = SingleResult._andMerge$LSingleResult$LSingleResult$LSingleResult$; + +/** + * @param {SingleResult} $this + * @param {SingleResult} result + * @param {SingleResult} rhs + */ +SingleResult._orMerge$LSingleResult$LSingleResult$LSingleResult$ = function ($this, result, rhs) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var id; + /** @type {SearchUnit} */ + var rhsSection; + /** @type {SearchUnit} */ + var lhsSection; + /** @type {Array.} */ + var unitIds$0; + /** @type {Array.} */ + var units$0; + result.unitIds = (unitIds$0 = $this.unitIds).slice(0, unitIds$0.length); + result.units = (units$0 = $this.units).slice(0, units$0.length); + for (i = 0; i < rhs.unitIds.length; i++) { + id = rhs.unitIds[i]; + rhsSection = rhs.units[i]; + if (result.unitIds.indexOf(id) !== -1) { + lhsSection = result.units[result.unitIds.indexOf(id)]; + SearchUnit$merge$LSearchUnit$LSearchUnit$(lhsSection, rhsSection); + } else { + result.unitIds.push(id); + result.units.push(rhsSection); + } + } +}; + +var SingleResult$_orMerge$LSingleResult$LSingleResult$LSingleResult$ = SingleResult._orMerge$LSingleResult$LSingleResult$LSingleResult$; + +/** + * @param {SingleResult} $this + * @param {SingleResult} result + * @param {SingleResult} rhs + */ +SingleResult._notMerge$LSingleResult$LSingleResult$LSingleResult$ = function ($this, result, rhs) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var id; + /** @type {SearchUnit} */ + var lhsSection; + for (i = 0; i < $this.unitIds.length; i++) { + id = $this.unitIds[i]; + if (rhs.unitIds.indexOf(id) === -1) { + lhsSection = $this.units[i]; + result.unitIds.push(id); + result.units.push(lhsSection); + } + } +}; + +var SingleResult$_notMerge$LSingleResult$LSingleResult$LSingleResult$ = SingleResult._notMerge$LSingleResult$LSingleResult$LSingleResult$; + +/** + * class SearchSummary extends Object + * @constructor + */ +function SearchSummary() { +} + +/** + * @constructor + */ +function SearchSummary$() { + this.sourceResults = [ ]; + this.result = null; + this.oktavia = null; +}; + +SearchSummary$.prototype = new SearchSummary; + +/** + * @constructor + * @param {Oktavia} oktavia + */ +function SearchSummary$LOktavia$(oktavia) { + this.sourceResults = [ ]; + this.result = null; + this.oktavia = oktavia; +}; + +SearchSummary$LOktavia$.prototype = new SearchSummary; + +/** + * @param {SearchSummary} $this + * @param {SingleResult} result + */ +SearchSummary.addQuery$LSearchSummary$LSingleResult$ = function ($this, result) { + $this.sourceResults.push(result); +}; + +var SearchSummary$addQuery$LSearchSummary$LSingleResult$ = SearchSummary.addQuery$LSearchSummary$LSingleResult$; + +/** + * @param {SearchSummary} $this + */ +SearchSummary.mergeResult$LSearchSummary$ = function ($this) { + $this.result = SearchSummary$mergeResult$LSearchSummary$ALSingleResult$($this, $this.sourceResults); +}; + +var SearchSummary$mergeResult$LSearchSummary$ = SearchSummary.mergeResult$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @param {Array.} results + * @return {SingleResult} + */ +SearchSummary.mergeResult$LSearchSummary$ALSingleResult$ = function ($this, results) { + /** @type {SingleResult} */ + var rhs; + /** @type {!number} */ + var i; + /** @type {!number} */ + var results$len$0; + rhs = results[0]; + for ((i = 1, results$len$0 = results.length); i < results$len$0; i++) { + rhs = SingleResult$merge$LSingleResult$LSingleResult$(rhs, results[i]); + } + return rhs; +}; + +var SearchSummary$mergeResult$LSearchSummary$ALSingleResult$ = SearchSummary.mergeResult$LSearchSummary$ALSingleResult$; + +/** + * @param {SearchSummary} $this + * @return {Array.} + */ +SearchSummary.getProposal$LSearchSummary$ = function ($this) { + /** @type {Array.} */ + var proposals; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var tmpSource; + /** @type {!number} */ + var j; + /** @type {SingleResult} */ + var result; + proposals = [ ]; + for (i = 0; i < $this.sourceResults.length; i++) { + tmpSource = [ ]; + for (j = 0; j < $this.sourceResults.length; j++) { + if (i !== j) { + tmpSource.push($this.sourceResults[j]); + } + } + result = SearchSummary$mergeResult$LSearchSummary$ALSingleResult$($this, tmpSource); + proposals.push(({omit: i, expect: result.units.length})); + } + proposals.sort((function (a, b) { + return b.expect - a.expect; + })); + return proposals; +}; + +var SearchSummary$getProposal$LSearchSummary$ = SearchSummary.getProposal$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @return {Array.} + */ +SearchSummary.getSortedResult$LSearchSummary$ = function ($this) { + /** @type {Array.} */ + var result; + /** @type {Array.} */ + var units$0; + result = (units$0 = $this.result.units).slice(0, units$0.length); + result.sort((function (a, b) { + return b.score - a.score; + })); + return result; +}; + +var SearchSummary$getSortedResult$LSearchSummary$ = SearchSummary.getSortedResult$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @return {!number} + */ +SearchSummary.size$LSearchSummary$ = function ($this) { + /** @type {SingleResult} */ + var this$0; + this$0 = $this.result; + return (this$0.units.length | 0); +}; + +var SearchSummary$size$LSearchSummary$ = SearchSummary.size$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @param {SingleResult} result + */ +SearchSummary.add$LSearchSummary$LSingleResult$ = function ($this, result) { + $this.sourceResults.push(result); +}; + +var SearchSummary$add$LSearchSummary$LSingleResult$ = SearchSummary.add$LSearchSummary$LSingleResult$; + +/** + * class Style extends Object + * @constructor + */ +function Style() { +} + +/** + * @constructor + * @param {!string} mode + */ +function Style$S(mode) { + this.styles = null; + this.escapeHTML = false; + switch (mode) { + case 'console': + this.styles = Style.console; + break; + case 'html': + this.styles = Style.html; + break; + case 'ignore': + this.styles = Style.ignore; + break; + default: + this.styles = Style.ignore; + break; + } + this.escapeHTML = mode === 'html'; +}; + +Style$S.prototype = new Style; + +/** + * @param {!string} source + * @return {!string} + */ +Style.prototype.convert$S = function (source) { + /** @type {_HTMLHandler} */ + var handler; + /** @type {SAXParser} */ + var parser; + handler = new _HTMLHandler$HASB(this.styles, this.escapeHTML); + parser = new SAXParser$LSAXHandler$(handler); + parser.parse$S(source); + return handler.text.join(''); +}; + +/** + * class Stemmer + * @constructor + */ +function Stemmer() { +} + +Stemmer.prototype.$__jsx_implements_Stemmer = true; + +/** + * @constructor + */ +function Stemmer$() { +}; + +Stemmer$.prototype = new Stemmer; + +/** + * class Metadata extends Object + * @constructor + */ +function Metadata() { +} + +/** + * @constructor + * @param {Oktavia} parent + */ +function Metadata$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); +}; + +Metadata$LOktavia$.prototype = new Metadata; + +/** + * @return {!number} + */ +Metadata.prototype._size$ = function () { + /** @type {BitVector} */ + var this$0; + /** @type {!number} */ + var i$0; + /** @type {BitVector} */ + var _bitVector$0; + this$0 = _bitVector$0 = this._bitVector; + i$0 = _bitVector$0._size; + return this$0.rank$IB(i$0, true); +}; + +/** + * @param {!number} index + * @return {!string} + */ +Metadata.prototype.getContent$I = function (index) { + /** @type {!number} */ + var startPosition; + /** @type {!number} */ + var length; + if (index < 0 || this._size$() <= index) { + throw new Error("Section.getContent() : range error " + (index + "")); + } + startPosition = 0; + if (index > 0) { + startPosition = this._bitVector.select$I(index - 1) + 1; + } + length = this._bitVector.select$I(index) - startPosition + 1; + return this._parent._getSubstring$II(startPosition, length); +}; + +/** + * @param {!number} index + * @return {!number} + */ +Metadata.prototype.getStartPosition$I = function (index) { + /** @type {!number} */ + var startPosition; + if (index < 0 || this._size$() <= index) { + throw new Error("Section.getContent() : range error " + (index + "")); + } + startPosition = 0; + if (index > 0) { + startPosition = this._bitVector.select$I(index - 1) + 1; + } + return (startPosition | 0); +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Metadata.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { +}; + +/** + * @param {!number} index + * @return {!string} + */ +Metadata.prototype.getInformation$I = function (index) { + return ''; +}; + +/** + */ +Metadata.prototype._build$ = function () { + this._bitVector.build$(); +}; + +/** + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Metadata.prototype._load$SSI = function (name, data, offset) { + offset = this._bitVector.load$SI(data, offset); + this._parent._metadataLabels.push(name); + this._parent._metadatas[name] = this; + return offset; +}; + +/** + * @return {!string} + */ +Metadata.prototype._dump$ = function () { + /** @type {BitVector} */ + var this$0; + /** @type {Array.} */ + var contents$0; + this$0 = this._bitVector; + contents$0 = [ ]; + contents$0.push(Binary$dump32bitNumber$N(this$0._size)); + contents$0.push(Binary$dump32bitNumberList$AN(this$0._v)); + return contents$0.join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Metadata.prototype._dump$LCompressionReport$ = function (report) { + /** @type {BitVector} */ + var this$0; + /** @type {Array.} */ + var contents$0; + this$0 = this._bitVector; + contents$0 = [ ]; + contents$0.push(Binary$dump32bitNumber$N(this$0._size)); + CompressionReport$add$LCompressionReport$II(report, 2, 2); + contents$0.push(Binary$dump32bitNumberList$ANLCompressionReport$(this$0._v, report)); + return contents$0.join(''); +}; + +/** + * class Section extends Metadata + * @constructor + */ +function Section() { +} + +Section.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + */ +function Section$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); + this._names = [ ]; +}; + +Section$LOktavia$.prototype = new Section; + +/** + * @param {!string} name + */ +Section.prototype.setTail$S = function (name) { + /** @type {!number} */ + var index$0; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + this$0 = this._parent; + this$0$0 = this$0._fmindex; + index$0 = this$0$0._substr.length; + this._names.push(name); + this._bitVector.set$I(index$0 - 1); +}; + +/** + * @param {!string} name + * @param {!number} index + */ +Section.prototype.setTail$SI = function (name, index) { + this._names.push(name); + this._bitVector.set$I(index - 1); +}; + +/** + * @return {!number} + */ +Section.prototype.size$ = function () { + return (this._names.length | 0); +}; + +/** + * @param {!number} position + * @return {!number} + */ +Section.prototype.getSectionIndex$I = function (position) { + /** @type {BitVector} */ + var this$0; + if (position < 0 || this._bitVector.size$() <= position) { + throw new Error("Section.getSectionIndex() : range error " + (position + "")); + } + this$0 = this._bitVector; + return this$0.rank$IB(position, true); +}; + +/** + * @param {!number} index + * @return {!string} + */ +Section.prototype.getName$I = function (index) { + if (index < 0 || this._names.length <= index) { + throw new Error("Section.getName() : range error"); + } + return this._names[index]; +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Section.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var position; + /** @type {!number} */ + var index; + /** @type {SearchUnit} */ + var unit; + for (i = 0; i < positions.length; i++) { + position = positions[i]; + index = this.getSectionIndex$I(position); + unit = SingleResult$getSearchUnit$LSingleResult$I(result, index); + if (unit.startPosition < 0) { + unit.startPosition = this.getStartPosition$I(index); + } + SearchUnit$addPosition$LSearchUnit$SIB(unit, word, position - unit.startPosition, stemmed); + } +}; + +/** + * @param {!number} index + * @return {!string} + */ +Section.prototype.getInformation$I = function (index) { + return this.getName$I(index); +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Section._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {LoadedStringListResult} */ + var strs; + /** @type {Section} */ + var section; + /** @type {!number} */ + var offset$0; + strs = new LoadedStringListResult$SI(data, offset); + section = new Section$LOktavia$(parent); + section._names = strs.result; + offset$0 = strs.offset; + offset$0 = section._bitVector.load$SI(data, offset$0); + section._parent._metadataLabels.push(name); + section._parent._metadatas[name] = section; + return offset$0; +}; + +var Section$_load$LOktavia$SSI = Section._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Section.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(0), Binary$dumpStringList$AS(this._names), Metadata.prototype._dump$.call(this) ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Section.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(0), Binary$dumpStringList$ASLCompressionReport$(this._names, report), Metadata.prototype._dump$LCompressionReport$.call(this, report) ].join(''); +}; + +/** + * class Splitter extends Metadata + * @constructor + */ +function Splitter() { +} + +Splitter.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + */ +function Splitter$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); + this.name = null; +}; + +Splitter$LOktavia$.prototype = new Splitter; + +/** + * @constructor + * @param {Oktavia} parent + * @param {!string} name + */ +function Splitter$LOktavia$S(parent, name) { + this._parent = parent; + this._bitVector = new BitVector$(); + this.name = name; +}; + +Splitter$LOktavia$S.prototype = new Splitter; + +/** + * @return {!number} + */ +Splitter.prototype.size$ = function () { + /** @type {BitVector} */ + var this$0$0; + /** @type {!number} */ + var i$0$0; + /** @type {BitVector} */ + var _bitVector$0; + this$0$0 = _bitVector$0 = this._bitVector; + i$0$0 = _bitVector$0._size; + return this$0$0.rank$IB(i$0$0, true); +}; + +/** + */ +Splitter.prototype.split$ = function () { + /** @type {!number} */ + var index$0; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + this$0 = this._parent; + this$0$0 = this$0._fmindex; + index$0 = this$0$0._substr.length; + this._bitVector.set$I(index$0 - 1); +}; + +/** + * @param {!number} index + */ +Splitter.prototype.split$I = function (index) { + this._bitVector.set$I(index - 1); +}; + +/** + * @param {!number} position + * @return {!number} + */ +Splitter.prototype.getIndex$I = function (position) { + /** @type {BitVector} */ + var this$0; + if (position < 0 || this._bitVector.size$() <= position) { + throw new Error("Section.getSectionIndex() : range error"); + } + this$0 = this._bitVector; + return this$0.rank$IB(position, true); +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Splitter.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var position; + /** @type {!number} */ + var index; + /** @type {SearchUnit} */ + var unit; + for (i = 0; i < positions.length; i++) { + position = positions[i]; + index = this.getIndex$I(position); + unit = SingleResult$getSearchUnit$LSingleResult$I(result, index); + if (unit.startPosition < 0) { + unit.startPosition = this.getStartPosition$I(index); + } + SearchUnit$addPosition$LSearchUnit$SIB(unit, word, position - unit.startPosition, stemmed); + } +}; + +/** + * @param {!number} index + * @return {!string} + */ +Splitter.prototype.getInformation$I = function (index) { + return (this.name != null ? this.name + (index + 1 + "") : ''); +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Splitter._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {Splitter} */ + var section; + section = new Splitter$LOktavia$(parent); + offset = section._bitVector.load$SI(data, offset); + section._parent._metadataLabels.push(name); + section._parent._metadatas[name] = section; + return offset; +}; + +var Splitter$_load$LOktavia$SSI = Splitter._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Splitter.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(1), Metadata.prototype._dump$.call(this) ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Splitter.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(1), Metadata.prototype._dump$LCompressionReport$.call(this, report) ].join(''); +}; + +/** + * class Table extends Metadata + * @constructor + */ +function Table() { +} + +Table.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + * @param {Array.} headers + */ +function Table$LOktavia$AS(parent, headers) { + this._parent = parent; + this._bitVector = new BitVector$(); + this._headers = headers; + this._columnTails = new BitVector$(); +}; + +Table$LOktavia$AS.prototype = new Table; + +/** + * @return {!number} + */ +Table.prototype.rowSize$ = function () { + /** @type {BitVector} */ + var this$0$0; + /** @type {!number} */ + var i$0$0; + /** @type {BitVector} */ + var _bitVector$0; + this$0$0 = _bitVector$0 = this._bitVector; + i$0$0 = _bitVector$0._size; + return this$0$0.rank$IB(i$0$0, true); +}; + +/** + * @return {!number} + */ +Table.prototype.columnSize$ = function () { + return (this._headers.length | 0); +}; + +/** + */ +Table.prototype.setColumnTail$ = function () { + /** @type {!number} */ + var index; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + /** @type {Oktavia} */ + var _parent$0; + this$0 = _parent$0 = this._parent; + this$0$0 = this$0._fmindex; + index = this$0$0._substr.length; + _parent$0._fmindex.push$S(Oktavia.eob); + this._columnTails.set$I(index - 1); +}; + +/** + */ +Table.prototype.setRowTail$ = function () { + /** @type {!number} */ + var index; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + this$0 = this._parent; + this$0$0 = this$0._fmindex; + index = this$0$0._substr.length; + this._bitVector.set$I(index - 1); +}; + +/** + * @param {!number} position + * @return {Array.} + */ +Table.prototype.getCell$I = function (position) { + /** @type {!number} */ + var row; + /** @type {!number} */ + var currentColumn; + /** @type {!number} */ + var lastRowColumn; + /** @type {!number} */ + var startPosition; + /** @type {Array.} */ + var result; + /** @type {BitVector} */ + var this$0; + /** @type {BitVector} */ + var this$1; + if (position < 0 || this._bitVector.size$() <= position) { + throw new Error("Section.getSectionIndex() : range error " + (position + "")); + } + this$0 = this._bitVector; + row = this$0.rank$IB(position, true); + this$1 = this._columnTails; + currentColumn = this$1.rank$IB(position, true); + lastRowColumn = 0; + if (row > 0) { + startPosition = this._bitVector.select$I(row - 1) + 1; + lastRowColumn = this._columnTails.rank$I(startPosition); + } + result = [ row, currentColumn - lastRowColumn ]; + return result; +}; + +/** + * @param {!number} rowIndex + * @return {Object.} + */ +Table.prototype.getRowContent$I = function (rowIndex) { + /** @type {!string} */ + var content; + /** @type {Array.} */ + var values; + /** @type {Object.} */ + var result; + /** @type {!number} */ + var i; + content = this.getContent$I(rowIndex); + values = content.split(Oktavia.eob, this._headers.length); + result = ({ }); + for (i in this._headers) { + if (i < values.length) { + result[this._headers[i]] = values[i]; + } else { + result[this._headers[i]] = ''; + } + } + return result; +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Table.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { +}; + +/** + * @param {!number} index + * @return {!string} + */ +Table.prototype.getInformation$I = function (index) { + return ''; +}; + +/** + */ +Table.prototype._build$ = function () { + this._bitVector.build$(); + this._columnTails.build$(); +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Table._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {LoadedStringListResult} */ + var strs; + /** @type {Table} */ + var table; + /** @type {!number} */ + var offset$0; + strs = new LoadedStringListResult$SI(data, offset); + table = new Table$LOktavia$AS(parent, strs.result); + offset$0 = strs.offset; + offset$0 = table._bitVector.load$SI(data, offset$0); + table._parent._metadataLabels.push(name); + table._parent._metadatas[name] = table; + offset = offset$0; + return table._columnTails.load$SI(data, offset$0); +}; + +var Table$_load$LOktavia$SSI = Table._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Table.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(2), Binary$dumpStringList$AS(this._headers), Metadata.prototype._dump$.call(this), this._columnTails.dump$() ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Table.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(2), Binary$dumpStringList$ASLCompressionReport$(this._headers, report), Metadata.prototype._dump$LCompressionReport$.call(this, report), this._columnTails.dump$LCompressionReport$(report) ].join(''); +}; + +/** + * class Block extends Metadata + * @constructor + */ +function Block() { +} + +Block.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + */ +function Block$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); + this._names = [ ]; + this._start = false; +}; + +Block$LOktavia$.prototype = new Block; + +/** + * @param {!string} blockName + */ +Block.prototype.startBlock$S = function (blockName) { + this.startBlock$SI(blockName, this._parent.contentSize$()); +}; + +/** + * @param {!string} blockName + * @param {!number} index + */ +Block.prototype.startBlock$SI = function (blockName, index) { + if (this._start) { + throw new Error('Splitter `' + this._names[this._names.length - 1] + '` is not closed'); + } + this._start = true; + this._names.push(blockName); + this._bitVector.set$I(index - 1); +}; + +/** + */ +Block.prototype.endBlock$ = function () { + this.endBlock$I(this._parent.contentSize$()); +}; + +/** + * @param {!number} index + */ +Block.prototype.endBlock$I = function (index) { + if (! this._start) { + throw new Error('Splitter is not started'); + } + this._start = false; + this._bitVector.set$I(index - 1); +}; + +/** + * @return {!number} + */ +Block.prototype.size$ = function () { + return (this._names.length | 0); +}; + +/** + * @param {!number} position + * @return {!number} + */ +Block.prototype.blockIndex$I = function (position) { + /** @type {!number} */ + var result; + /** @type {BitVector} */ + var this$0; + if (position < 0 || this._parent._fmindex.size$() - 1 <= position) { + throw new Error("Block.blockIndex() : range error " + (position + "")); + } + if (position >= this._bitVector.size$()) { + position = (this._bitVector.size$() - 1 | 0); + result = (this._bitVector.rank$I(position) + 1 | 0); + } else { + this$0 = this._bitVector; + result = this$0.rank$IB(position, true); + } + return result; +}; + +/** + * @param {!number} position + * @return {!boolean} + */ +Block.prototype.inBlock$I = function (position) { + /** @type {!number} */ + var blockIndex; + blockIndex = this.blockIndex$I(position); + return blockIndex % 2 !== 0; +}; + +/** + * @param {!number} position + * @return {!string} + */ +Block.prototype.getBlockContent$I = function (position) { + /** @type {!number} */ + var blockIndex; + /** @type {!string} */ + var result; + blockIndex = this.blockIndex$I(position); + if (blockIndex % 2 !== 0) { + result = this.getContent$I(blockIndex); + } else { + result = ''; + } + return result; +}; + +/** + * @param {!number} position + * @return {!string} + */ +Block.prototype.getBlockName$I = function (position) { + /** @type {!number} */ + var blockIndex; + /** @type {!string} */ + var result; + blockIndex = this.blockIndex$I(position); + if (blockIndex % 2 !== 0) { + result = this._names[blockIndex >>> 1]; + } else { + result = ''; + } + return result; +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Block.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { +}; + +/** + * @param {!number} index + * @return {!string} + */ +Block.prototype.getInformation$I = function (index) { + return ''; +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Block._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {LoadedStringListResult} */ + var strs; + /** @type {Block} */ + var block; + /** @type {!number} */ + var offset$0; + strs = new LoadedStringListResult$SI(data, offset); + block = new Block$LOktavia$(parent); + block._names = strs.result; + offset$0 = strs.offset; + offset$0 = block._bitVector.load$SI(data, offset$0); + block._parent._metadataLabels.push(name); + block._parent._metadatas[name] = block; + return offset$0; +}; + +var Block$_load$LOktavia$SSI = Block._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Block.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(3), Binary$dumpStringList$AS(this._names), Metadata.prototype._dump$.call(this) ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Block.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(3), Binary$dumpStringList$ASLCompressionReport$(this._names, report), Metadata.prototype._dump$LCompressionReport$.call(this, report) ].join(''); +}; + +/** + * class FMIndex extends Object + * @constructor + */ +function FMIndex() { +} + +/** + * @constructor + */ +function FMIndex$() { + /** @type {Array.} */ + var _rlt$0; + this._ssize = 0; + (this._ddic = 0, this._head = 0); + this._substr = ""; + this._sv = new WaveletMatrix$(); + this._posdic = [ ]; + this._idic = [ ]; + _rlt$0 = this._rlt = [ ]; + _rlt$0.length = 65536; +}; + +FMIndex$.prototype = new FMIndex; + +/** + */ +FMIndex.prototype.clear$ = function () { + /** @type {WaveletMatrix} */ + var this$0; + this$0 = this._sv; + this$0._bv.length = 0; + this$0._seps.length = 0; + this$0._size = 0; + this._posdic.length = 0; + this._idic.length = 0; + this._ddic = 0; + this._head = 0; + this._substr = ""; +}; + +/** + * @return {!number} + */ +FMIndex.prototype.size$ = function () { + /** @type {WaveletMatrix} */ + var this$0; + this$0 = this._sv; + return this$0._size; +}; + +/** + * @return {!number} + */ +FMIndex.prototype.contentSize$ = function () { + return this._substr.length; +}; + +/** + * @param {!string} key + * @return {!number} + */ +FMIndex.prototype.getRows$S = function (key) { + /** @type {Array.} */ + var pos; + pos = [ ]; + return this.getRows$SAI(key, pos); +}; + +/** + * @param {!string} key + * @param {Array.} pos + * @return {!number} + */ +FMIndex.prototype.getRows$SAI = function (key, pos) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {!number} */ + var first; + /** @type {undefined|!number} */ + var last; + /** @type {!number} */ + var c; + /** @type {Array.} */ + var _rlt$0; + i = key.length - 1; + code = key.charCodeAt(i); + first = (_rlt$0 = this._rlt)[code] + 1; + last = _rlt$0[code + 1]; + while (first <= last) { + if (i === 0) { + pos[0] = (-- first | 0); + pos[1] = -- last; + return (last - first + 1 | 0); + } + i--; + c = key.charCodeAt(i); + first = this._rlt[c] + this._sv.rank$II(first - 1, c) + 1; + last = this._rlt[c] + this._sv.rank$II(last, c); + } + return 0; +}; + +/** + * @param {!number} i + * @return {!number} + */ +FMIndex.prototype.getPosition$I = function (i) { + /** @type {!number} */ + var pos; + /** @type {!number} */ + var c; + if (i >= this.size$()) { + throw new Error("FMIndex.getPosition() : range error"); + } + pos = 0; + while (i !== this._head) { + if (i % this._ddic === 0) { + pos += this._posdic[i / this._ddic] + 1; + break; + } + c = this._sv.get$I(i); + i = this._rlt[c] + this._sv.rank$II(i, c); + pos++; + } + return (pos % this.size$() | 0); +}; + +/** + * @param {!number} pos + * @param {!number} len + * @return {!string} + */ +FMIndex.prototype.getSubstring$II = function (pos, len) { + /** @type {!number} */ + var pos_end; + /** @type {!number} */ + var pos_tmp; + /** @type {!number} */ + var i; + /** @type {!number} */ + var pos_idic; + /** @type {!string} */ + var substr; + /** @type {!number} */ + var c; + /** @type {!number} */ + var _ddic$0; + if (pos >= this.size$()) { + throw new Error("FMIndex.getSubstring() : range error"); + } + pos_end = Math.min(pos + len, this.size$()); + pos_tmp = this.size$() - 1; + i = this._head; + pos_idic = Math.floor((pos_end + (_ddic$0 = this._ddic) - 2) / _ddic$0); + if (pos_idic < this._idic.length) { + pos_tmp = pos_idic * this._ddic; + i = this._idic[pos_idic]; + } + substr = ""; + while (pos_tmp >= pos) { + c = this._sv.get$I(i); + i = this._rlt[c] + this._sv.rank$II(i, c); + if (pos_tmp < pos_end) { + substr = String.fromCharCode(c) + substr; + } + if (pos_tmp === 0) { + break; + } + pos_tmp--; + } + return substr; +}; + +/** + */ +FMIndex.prototype.build$ = function () { + this.build$SIIB(String.fromCharCode(0), 65535, 20, false); +}; + +/** + * @param {!string} end_marker + * @param {!number} ddic + * @param {!boolean} verbose + */ +FMIndex.prototype.build$SIB = function (end_marker, ddic, verbose) { + this.build$SIIB(end_marker, 65535, ddic, verbose); +}; + +/** + * @param {!string} end_marker + * @param {!number} maxChar + * @param {!number} ddic + * @param {!boolean} verbose + */ +FMIndex.prototype.build$SIIB = function (end_marker, maxChar, ddic, verbose) { + /** @type {BurrowsWheelerTransform} */ + var b; + /** @type {!string} */ + var s; + /** @type {!number} */ + var c; + /** @type {!string} */ + var str$0; + /** @type {WaveletMatrix} */ + var this$0; + /** @type {!string} */ + var _str$0; + /** @type {Array.} */ + var _suffixarray$0; + if (verbose) { + console.time("building burrows-wheeler transform"); + } + this._substr += end_marker; + b = ({_str: "", _size: 0, _head: 0, _suffixarray: [ ]}); + str$0 = this._substr; + _str$0 = b._str = str$0; + b._size = _str$0.length; + _suffixarray$0 = b._suffixarray = SAIS$make$S(str$0); + b._head = (_suffixarray$0.indexOf(0) | 0); + s = BurrowsWheelerTransform$get$LBurrowsWheelerTransform$(b); + this._ssize = s.length; + this._head = b._head; + b._str = ""; + b._size = 0; + b._head = 0; + b._suffixarray.length = 0; + this._substr = ""; + if (verbose) { + console.timeEnd("building burrows-wheeler transform"); + } + if (verbose) { + console.time("building wavelet matrix"); + } + this$0 = this._sv; + this$0._bitsize = (Math.ceil(Math.log(maxChar) / 0.6931471805599453) | 0); + if (verbose) { + console.log(" maxCharCode: ", maxChar); + console.log(" bitSize: ", this._sv.bitsize$()); + } + this._sv.build$S(s); + if (verbose) { + console.timeEnd("building wavelet matrix"); + } + if (verbose) { + console.time("caching rank less than"); + } + for (c = 0; c < maxChar; c++) { + this._rlt[c] = this._sv.rank_less_than$II(this._sv.size$(), c); + } + if (verbose) { + console.timeEnd("caching rank less than"); + } + this._ddic = ddic; + if (verbose) { + console.time("building dictionaries"); + } + this._buildDictionaries$(); + if (verbose) { + console.timeEnd("building dictionaries"); + console.log(''); + } +}; + +/** + */ +FMIndex.prototype._buildDictionaries$ = function () { + /** @type {!number} */ + var i; + /** @type {!number} */ + var pos; + /** @type {!number} */ + var c; + for (i = 0; i < this._ssize / this._ddic + 1; i++) { + this._posdic.push(0); + this._idic.push(0); + } + i = this._head; + pos = this.size$() - 1; + do { + if (i % this._ddic === 0) { + this._posdic[Math.floor(i / this._ddic)] = (pos | 0); + } + if (pos % this._ddic === 0) { + this._idic[Math.floor(pos / this._ddic)] = (i | 0); + } + c = this._sv.get$I(i); + i = this._rlt[c] + this._sv.rank$II(i, c); + pos--; + } while (i !== this._head); +}; + +/** + * @param {!string} doc + */ +FMIndex.prototype.push$S = function (doc) { + if (doc.length <= 0) { + throw new Error("FMIndex::push(): empty string"); + } + this._substr += doc; +}; + +/** + * @param {!string} keyword + * @return {Array.} + */ +FMIndex.prototype.search$S = function (keyword) { + /** @type {Array.} */ + var result; + /** @type {Array.} */ + var position; + /** @type {!number} */ + var rows; + /** @type {undefined|!number} */ + var first; + /** @type {undefined|!number} */ + var last; + /** @type {undefined|!number} */ + var i; + result = [ ]; + position = [ ]; + rows = this.getRows$SAI(keyword, position); + if (rows > 0) { + first = position[0]; + last = position[1]; + for (i = first; i <= last; i++) { + result.push(this.getPosition$I(i)); + } + } + return result; +}; + +/** + * @return {!string} + */ +FMIndex.prototype.dump$ = function () { + return this.dump$B(false); +}; + +/** + * @param {!boolean} verbose + * @return {!string} + */ +FMIndex.prototype.dump$B = function (verbose) { + /** @type {Array.} */ + var contents; + /** @type {CompressionReport} */ + var report; + /** @type {!number} */ + var i; + contents = [ ]; + report = ({source: 0, result: 0}); + contents.push(Binary$dump32bitNumber$N(this._ddic)); + contents.push(Binary$dump32bitNumber$N(this._ssize)); + contents.push(Binary$dump32bitNumber$N(this._head)); + CompressionReport$add$LCompressionReport$II(report, 6, 6); + contents.push(this._sv.dump$LCompressionReport$(report)); + if (verbose) { + console.log("Serializing FM-index"); + console.log(' Wavelet Matrix: ' + (contents[3].length * 2 + "") + ' bytes (' + (Math.round(report.result * 100.0 / report.source) + "") + '%)'); + } + contents.push(Binary$dump32bitNumber$N(this._posdic.length)); + for (i in this._posdic) { + contents.push(Binary$dump32bitNumber$N(this._posdic[i])); + } + for (i in this._idic) { + contents.push(Binary$dump32bitNumber$N(this._idic[i])); + } + if (verbose) { + console.log(' Dictionary Cache: ' + (this._idic.length * 16 + "") + ' bytes'); + } + return contents.join(""); +}; + +/** + * @param {!string} data + * @return {!number} + */ +FMIndex.prototype.load$S = function (data) { + return this.load$SI(data, 0); +}; + +/** + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +FMIndex.prototype.load$SI = function (data, offset) { + /** @type {!number} */ + var maxChar; + /** @type {!number} */ + var c; + /** @type {!number} */ + var size; + /** @type {!number} */ + var i; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var result$1; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + this._ddic = (result$0 | 0); + this._ssize = (Binary$load32bitNumber$SI(data, offset + 2) | 0); + this._head = (Binary$load32bitNumber$SI(data, offset + 4) | 0); + offset = this._sv.load$SI(data, offset + 6); + maxChar = Math.pow(2, this._sv.bitsize$()); + for (c = 0; c < maxChar; c++) { + this._rlt[c] = this._sv.rank_less_than$II(this._sv.size$(), c); + } + result$1 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + size = result$1; + offset += 2; + for (i = 0; i < size; (i++, offset += 2)) { + this._posdic.push(Binary$load32bitNumber$SI(data, offset)); + } + for (i = 0; i < size; (i++, offset += 2)) { + this._idic.push(Binary$load32bitNumber$SI(data, offset)); + } + return offset; +}; + +/** + * class Tag extends Object + * @constructor + */ +function Tag() { +} + +/** + * @constructor + * @param {!string} name + */ +function Tag$S(name) { + this.name = name; + this.attributes = ({ }); + this.isSelfClosing = false; +}; + +Tag$S.prototype = new Tag; + +/** + * class _Common extends Object + * @constructor + */ +function _Common() { +} + +/** + * @constructor + */ +function _Common$() { +}; + +_Common$.prototype = new _Common; + +/** + * class _State extends Object + * @constructor + */ +function _State() { +} + +/** + * @constructor + */ +function _State$() { +}; + +_State$.prototype = new _State; + +/** + * class SAXHandler extends Object + * @constructor + */ +function SAXHandler() { +} + +/** + * @constructor + */ +function SAXHandler$() { + this.position = 0; + this.column = 0; + this.line = 0; +}; + +SAXHandler$.prototype = new SAXHandler; + +/** + * @param {Error} error + */ +SAXHandler.prototype.onerror$LError$ = function (error) { +}; + +/** + * @param {!string} text + */ +SAXHandler.prototype.ontext$S = function (text) { +}; + +/** + * @param {!string} doctype + */ +SAXHandler.prototype.ondoctype$S = function (doctype) { +}; + +/** + * @param {!string} name + * @param {!string} body + */ +SAXHandler.prototype.onprocessinginstruction$SS = function (name, body) { +}; + +/** + * @param {!string} sgmlDecl + */ +SAXHandler.prototype.onsgmldeclaration$S = function (sgmlDecl) { +}; + +/** + * @param {!string} tagname + * @param {Object.} attributes + */ +SAXHandler.prototype.onopentag$SHS = function (tagname, attributes) { +}; + +/** + * @param {!string} tagname + */ +SAXHandler.prototype.onclosetag$S = function (tagname) { +}; + +/** + * @param {!string} name + * @param {!string} value + */ +SAXHandler.prototype.onattribute$SS = function (name, value) { +}; + +/** + * @param {!string} comment + */ +SAXHandler.prototype.oncomment$S = function (comment) { +}; + +/** + */ +SAXHandler.prototype.onopencdata$ = function () { +}; + +/** + * @param {!string} cdata + */ +SAXHandler.prototype.oncdata$S = function (cdata) { +}; + +/** + */ +SAXHandler.prototype.onclosecdata$ = function () { +}; + +/** + */ +SAXHandler.prototype.onend$ = function () { +}; + +/** + */ +SAXHandler.prototype.onready$ = function () { +}; + +/** + * @param {!string} script + */ +SAXHandler.prototype.onscript$S = function (script) { +}; + +/** + * class _HTMLHandler extends SAXHandler + * @constructor + */ +function _HTMLHandler() { +} + +_HTMLHandler.prototype = new SAXHandler; +/** + * @constructor + * @param {Object.>} styles + * @param {!boolean} escape + */ +function _HTMLHandler$HASB(styles, escape) { + this.position = 0; + this.column = 0; + this.line = 0; + this.text = [ ]; + this.escape = escape; + this.styles = styles; +}; + +_HTMLHandler$HASB.prototype = new _HTMLHandler; + +/** + * @param {!string} str + * @return {!string} + */ +_HTMLHandler.escapeHTML$S = function (str) { + return str.replace(/\n/g, "
").replace(/&/g, "&").replace(/"/g, """).replace(//g, ">"); +}; + +var _HTMLHandler$escapeHTML$S = _HTMLHandler.escapeHTML$S; + +/** + * @param {!string} tagname + * @param {Object.} attributes + */ +_HTMLHandler.prototype.onopentag$SHS = function (tagname, attributes) { + this.text.push(this.styles[tagname][0]); +}; + +/** + * @param {!string} tagname + */ +_HTMLHandler.prototype.onclosetag$S = function (tagname) { + this.text.push(this.styles[tagname][1]); +}; + +/** + * @param {!string} text + */ +_HTMLHandler.prototype.ontext$S = function (text) { + if (this.escape) { + this.text.push(text.replace(/\n/g, "
").replace(/&/g, "&").replace(/"/g, """).replace(//g, ">")); + } else { + this.text.push(text); + } +}; + +/** + * @return {!string} + */ +_HTMLHandler.prototype.result$ = function () { + return this.text.join(''); +}; + +/** + * class SAXParser extends Object + * @constructor + */ +function SAXParser() { +} + +/** + * @constructor + * @param {SAXHandler} handler + */ +function SAXParser$LSAXHandler$(handler) { + this.q = ""; + this.c = ""; + this.bufferCheckPosition = 0; + this.looseCase = ""; + this.tags = [ ]; + this.closed = false; + this.closedRoot = false; + this.sawRoot = false; + this.tag = null; + this.error = null; + this.handler = null; + this.ENTITIES = null; + this.strict = false; + this.tagName = ""; + this.state = 0; + this.line = 0; + this.column = 0; + this.position = 0; + this.startTagPosition = 0; + this.attribName = ""; + this.attribValue = ""; + this.script = ""; + this.textNode = ""; + this.attribList = null; + this.noscript = false; + this.cdata = ""; + this.procInstBody = ""; + this.procInstName = ""; + this.doctype = ""; + this.entity = ""; + this.sgmlDecl = ""; + this.comment = ""; + this.preTags = 0; + this._init$LSAXHandler$B(handler, false); +}; + +SAXParser$LSAXHandler$.prototype = new SAXParser; + +/** + * @constructor + * @param {SAXHandler} handler + * @param {!boolean} strict + */ +function SAXParser$LSAXHandler$B(handler, strict) { + this.q = ""; + this.c = ""; + this.bufferCheckPosition = 0; + this.looseCase = ""; + this.tags = [ ]; + this.closed = false; + this.closedRoot = false; + this.sawRoot = false; + this.tag = null; + this.error = null; + this.handler = null; + this.ENTITIES = null; + this.strict = false; + this.tagName = ""; + this.state = 0; + this.line = 0; + this.column = 0; + this.position = 0; + this.startTagPosition = 0; + this.attribName = ""; + this.attribValue = ""; + this.script = ""; + this.textNode = ""; + this.attribList = null; + this.noscript = false; + this.cdata = ""; + this.procInstBody = ""; + this.procInstName = ""; + this.doctype = ""; + this.entity = ""; + this.sgmlDecl = ""; + this.comment = ""; + this.preTags = 0; + this._init$LSAXHandler$B(handler, strict); +}; + +SAXParser$LSAXHandler$B.prototype = new SAXParser; + +/** + * @param {SAXHandler} handler + * @param {!boolean} strict + */ +SAXParser.prototype._init$LSAXHandler$B = function (handler, strict) { + this.handler = handler; + this.clearBuffers$(); + this.q = ""; + this.bufferCheckPosition = 65536; + this.looseCase = 'toLowerCase'; + this.tags = [ ]; + this.closed = this.closedRoot = this.sawRoot = false; + this.tag = null; + this.error = null; + this.strict = strict; + this.noscript = strict; + this.state = 1; + this.ENTITIES = _Entities$entity_list$(); + this.attribList = [ ]; + this.noscript = false; + this.preTags = 0; +}; + +/** + * @param {!boolean} flag + */ +SAXParser.prototype.set_noscript$B = function (flag) { + this.noscript = flag; +}; + +/** + * @return {SAXParser} + */ +SAXParser.prototype.resume$ = function () { + this.error = null; + return this; +}; + +/** + * @return {SAXParser} + */ +SAXParser.prototype.close$ = function () { + return this.parse$S(''); +}; + +/** + * @param {!string} chunk + * @return {SAXParser} + */ +SAXParser.prototype.parse$S = function (chunk) { + /** @type {Char} */ + var _; + /** @type {!number} */ + var i; + /** @type {!string} */ + var c; + /** @type {!number} */ + var starti; + /** @type {!number} */ + var pad; + /** @type {!number} */ + var returnState; + /** @type {Object.} */ + var charclass$0; + /** @type {Object.} */ + var charclass$1; + /** @type {RegExp} */ + var charclass$2; + /** @type {Object.} */ + var charclass$3; + /** @type {Object.} */ + var charclass$4; + /** @type {Object.} */ + var charclass$5; + /** @type {!string} */ + var text$0; + /** @type {Object.} */ + var charclass$6; + /** @type {RegExp} */ + var charclass$7; + /** @type {Object.} */ + var charclass$8; + /** @type {Object.} */ + var charclass$9; + /** @type {RegExp} */ + var charclass$10; + /** @type {Object.} */ + var charclass$11; + /** @type {RegExp} */ + var charclass$12; + /** @type {Object.} */ + var charclass$13; + /** @type {RegExp} */ + var charclass$14; + /** @type {Object.} */ + var charclass$15; + /** @type {Object.} */ + var charclass$16; + /** @type {Object.} */ + var charclass$17; + /** @type {Object.} */ + var charclass$18; + /** @type {RegExp} */ + var charclass$19; + /** @type {RegExp} */ + var charclass$20; + /** @type {Object.} */ + var charclass$21; + /** @type {Object.} */ + var charclass$22; + /** @type {Object.} */ + var charclass$23; + /** @type {Object.} */ + var charclass$24; + /** @type {!string} */ + var comment$0; + _ = new Char$(); + if (this.error) { + throw this.error; + } + if (this.closed) { + return this.emiterror$S("Cannot write after close. Assign an onready handler."); + } + (i = 0, c = ""); + while (this.c = c = chunk.charAt(i++)) { + this.position++; + if (c === "\n") { + this.handler.line++; + this.handler.column = 0; + } else { + this.handler.column++; + } + switch (this.state) { + case 1: + if (c === "<") { + this.state = 4; + this.startTagPosition = this.position; + } else { + charclass$0 = _.whitespace; + if (! $__jsx_ObjectHasOwnProperty.call(charclass$0, c)) { + this.strictFail$S("Non-whitespace before first tag."); + this.textNode = c; + this.state = 2; + } + } + continue; + case 2: + if (this.sawRoot && ! this.closedRoot) { + starti = i - 1; + while (c && c !== "<" && c !== "&") { + c = chunk.charAt(i++); + if (c) { + this.position++; + if (c === "\n") { + this.handler.line++; + this.handler.column = 0; + } else { + this.handler.column++; + } + } + } + this.textNode += chunk.substring(starti, i - 1); + } + if (c === "<") { + this.state = 4; + this.startTagPosition = this.position; + } else { + if (_.not$HBS(_.whitespace, c) && (! this.sawRoot || this.closedRoot)) { + this.strictFail$S("Text data outside of root node."); + } + if (c === "&") { + this.state = 3; + } else { + this.textNode += c; + } + } + continue; + case 33: + if (c === "<") { + this.state = 34; + } else { + this.script += c; + } + continue; + case 34: + if (c === "/") { + this.state = 31; + } else { + this.script += "<" + c; + this.state = 33; + } + continue; + case 4: + if (c === "!") { + this.state = 5; + this.sgmlDecl = ""; + } else { + charclass$1 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$1, c)) { + } else { + charclass$2 = _.nameStart; + if (charclass$2.test(c)) { + this.state = 21; + this.tagName = c; + } else { + if (c === "/") { + this.state = 31; + this.tagName = ""; + } else { + if (c === "?") { + this.state = 18; + this.procInstName = this.procInstBody = ""; + } else { + this.strictFail$S("Unencoded <"); + if (this.startTagPosition + 1 < this.position) { + pad = this.position - this.startTagPosition; + for (i = 0; i < pad; i++) { + c = " " + c; + } + } + this.textNode += "<" + c; + this.state = 2; + } + } + } + } + } + continue; + case 5: + if ((this.sgmlDecl + c).toUpperCase() === _.CDATA) { + this.closetext_if_exist$(); + this.state = 15; + this.sgmlDecl = ""; + this.cdata = ""; + } else { + if (this.sgmlDecl + c === "--") { + this.state = 12; + this.comment = ""; + this.sgmlDecl = ""; + } else { + if ((this.sgmlDecl + c).toUpperCase() === _.DOCTYPE) { + this.state = 7; + if (this.doctype || this.sawRoot) { + this.strictFail$S("Inappropriately located doctype declaration"); + } + this.doctype = ""; + this.sgmlDecl = ""; + } else { + if (c === ">") { + this.closetext_if_exist$(); + this.sgmlDecl = ""; + this.state = 2; + } else { + charclass$3 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$3, c)) { + this.state = 6; + this.sgmlDecl += c; + } else { + this.sgmlDecl += c; + } + } + } + } + } + continue; + case 6: + if (c === this.q) { + this.state = 5; + this.q = ""; + } + this.sgmlDecl += c; + continue; + case 7: + if (c === ">") { + this.state = 2; + this.closetext_if_exist$(); + this.doctype.trim(); + } else { + this.doctype += c; + if (c === "[") { + this.state = 9; + } else { + charclass$4 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$4, c)) { + this.state = 8; + this.q = c; + } + } + } + continue; + case 8: + this.doctype += c; + if (c === this.q) { + this.q = ""; + this.state = 7; + } + continue; + case 9: + this.doctype += c; + if (c === "]") { + this.state = 7; + } else { + charclass$5 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$5, c)) { + this.state = 10; + this.q = c; + } + } + continue; + case 10: + this.doctype += c; + if (c === this.q) { + this.state = 9; + this.q = ""; + } + continue; + case 12: + if (c === "-") { + this.state = 13; + } else { + this.comment += c; + } + continue; + case 13: + if (c === "-") { + this.state = 14; + text$0 = this.comment; + text$0 = text$0.replace(/[\n\t]/g, ' '); + text$0 = text$0.replace(/\s\s+/g, " "); + comment$0 = this.comment = text$0; + if (comment$0) { + this.closetext_if_exist$(); + this.comment.trim(); + } + this.comment = ""; + } else { + this.comment += "-" + c; + this.state = 12; + } + continue; + case 14: + if (c !== ">") { + this.strictFail$S("Malformed comment"); + this.comment += "--" + c; + this.state = 12; + } else { + this.state = 2; + } + continue; + case 15: + if (c === "]") { + this.state = 16; + } else { + this.cdata += c; + } + continue; + case 16: + if (c === "]") { + this.state = 17; + } else { + this.cdata += "]" + c; + this.state = 15; + } + continue; + case 17: + if (c === ">") { + if (this.cdata) { + this.closetext_if_exist$(); + } + this.cdata = ""; + this.state = 2; + } else { + if (c === "]") { + this.cdata += "]"; + } else { + this.cdata += "]]" + c; + this.state = 15; + } + } + continue; + case 18: + if (c === "?") { + this.state = 20; + } else { + charclass$6 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$6, c)) { + this.state = 19; + } else { + this.procInstName += c; + } + } + continue; + case 19: + if (! this.procInstBody && _.is$HBS(_.whitespace, c)) { + continue; + } else { + if (c === "?") { + this.state = 20; + } else { + this.procInstBody += c; + } + } + continue; + case 20: + if (c === ">") { + this.closetext_if_exist$(); + this.procInstName = this.procInstBody = ""; + this.state = 2; + } else { + this.procInstBody += "?" + c; + this.state = 19; + } + continue; + case 21: + charclass$7 = _.nameBody; + if (charclass$7.test(c)) { + this.tagName += c; + } else { + this.newTag$(); + if (c === ">") { + this.openTag$B(false); + } else { + if (c === "/") { + this.state = 22; + } else { + charclass$8 = _.whitespace; + if (! $__jsx_ObjectHasOwnProperty.call(charclass$8, c)) { + this.strictFail$S("Invalid character in tag name"); + } + this.state = 23; + } + } + } + continue; + case 22: + if (c === ">") { + this.openTag$B(true); + this.closeTag$(); + } else { + this.strictFail$S("Forward-slash in opening tag not followed by >"); + this.state = 23; + } + continue; + case 23: + charclass$9 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$9, c)) { + continue; + } else { + if (c === ">") { + this.openTag$B(false); + } else { + if (c === "/") { + this.state = 22; + } else { + charclass$10 = _.nameStart; + if (charclass$10.test(c)) { + this.attribName = c; + this.attribValue = ""; + this.state = 24; + } else { + this.strictFail$S("Invalid attribute name"); + } + } + } + } + continue; + case 24: + if (c === "=") { + this.state = 26; + } else { + if (c === ">") { + this.strictFail$S("Attribute without value"); + this.attribValue = this.attribName; + this.attrib$(); + this.openTag$B(false); + } else { + charclass$11 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$11, c)) { + this.state = 25; + } else { + charclass$12 = _.nameBody; + if (charclass$12.test(c)) { + this.attribName += c; + } else { + this.strictFail$S("Invalid attribute name"); + } + } + } + } + continue; + case 25: + if (c === "=") { + this.state = 26; + } else { + charclass$13 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$13, c)) { + continue; + } else { + this.strictFail$S("Attribute without value"); + this.tag.attributes[this.attribName] = ""; + this.attribValue = ""; + this.closetext_if_exist$(); + this.attribName = ""; + if (c === ">") { + this.openTag$B(false); + } else { + charclass$14 = _.nameStart; + if (charclass$14.test(c)) { + this.attribName = c; + this.state = 24; + } else { + this.strictFail$S("Invalid attribute name"); + this.state = 23; + } + } + } + } + continue; + case 26: + charclass$15 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$15, c)) { + continue; + } else { + charclass$16 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$16, c)) { + this.q = c; + this.state = 27; + } else { + this.strictFail$S("Unquoted attribute value"); + this.state = 28; + this.attribValue = c; + } + } + continue; + case 27: + if (c !== this.q) { + if (c === "&") { + this.state = 29; + } else { + this.attribValue += c; + } + continue; + } + this.attrib$(); + this.q = ""; + this.state = 23; + continue; + case 28: + charclass$17 = _.attribEnd; + if (! $__jsx_ObjectHasOwnProperty.call(charclass$17, c)) { + if (c === "&") { + this.state = 30; + } else { + this.attribValue += c; + } + continue; + } + this.attrib$(); + if (c === ">") { + this.openTag$B(false); + } else { + this.state = 23; + } + continue; + case 31: + if (! this.tagName) { + charclass$18 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$18, c)) { + continue; + } else { + charclass$19 = _.nameStart; + if (! charclass$19.test(c)) { + if (this.script) { + this.script += "") { + this.closeTag$(); + } else { + charclass$20 = _.nameBody; + if (charclass$20.test(c)) { + this.tagName += c; + } else { + if (this.script) { + this.script += "") { + this.closeTag$(); + } else { + this.strictFail$S("Invalid characters in closing tag"); + } + continue; + case 3: + if (c === ";") { + this.textNode += this.parseEntity$(); + this.entity = ""; + this.state = 2; + } else { + charclass$23 = _.entity; + if ($__jsx_ObjectHasOwnProperty.call(charclass$23, c)) { + this.entity += c; + } else { + this.strictFail$S("Invalid character entity"); + this.textNode += "&" + this.entity + c; + this.entity = ""; + this.state = 2; + } + } + continue; + case 29: + case 30: + if (this.state === 29) { + returnState = 27; + } else { + returnState = 28; + } + if (c === ";") { + this.attribValue += this.parseEntity$(); + this.entity = ""; + this.state = (returnState | 0); + } else { + charclass$24 = _.entity; + if ($__jsx_ObjectHasOwnProperty.call(charclass$24, c)) { + this.entity += c; + } else { + this.strictFail$S("Invalid character entity"); + this.attribValue += "&" + this.entity + c; + this.entity = ""; + this.state = (returnState | 0); + } + } + continue; + default: + throw new Error("Unknown state: " + (this.state + "")); + } + } + this.end$(); + return this; +}; + +/** + */ +SAXParser.prototype.clearBuffers$ = function () { + this.comment = ''; + this.sgmlDecl = ''; + this.textNode = ''; + this.tagName = ''; + this.doctype = ''; + this.procInstName = ''; + this.procInstBody = ''; + this.entity = ''; + this.attribName = ''; + this.attribValue = ''; + this.cdata = ''; + this.script = ''; +}; + +/** + */ +SAXParser.prototype.closetext_if_exist$ = function () { + if (this.textNode !== '') { + this.closetext$(); + } +}; + +/** + */ +SAXParser.prototype.closetext$ = function () { + /** @type {!string} */ + var text; + /** @type {!string} */ + var text$0; + if (this.preTags === 0) { + text$0 = this.textNode; + text$0 = text$0.replace(/[\n\t]/g, ' '); + text$0 = text$0.replace(/\s\s+/g, " "); + text = text$0; + if (text$0) { + this.handler.ontext$S(text); + } + } else { + if (this.textNode) { + this.handler.ontext$S(this.textNode); + } + } + this.textNode = ""; +}; + +/** + * @param {!string} text + * @return {!string} + */ +SAXParser.prototype.textopts$S = function (text) { + text = text.replace(/[\n\t]/g, ' '); + text = text.replace(/\s\s+/g, " "); + return text; +}; + +/** + * @param {!string} er + * @return {SAXParser} + */ +SAXParser.prototype.emiterror$S = function (er) { + /** @type {Error} */ + var error; + this.closetext$(); + er += "\nLine: " + (this.line + "") + "\nColumn: " + (this.column + "") + "\nChar: " + this.c; + error = new Error(er); + this.error = error; + return this; +}; + +/** + */ +SAXParser.prototype.end$ = function () { + if (! this.closedRoot) { + this.strictFail$S("Unclosed root tag"); + } + if (this.state !== 2) { + this.emiterror$S("Unexpected end"); + } + this.closetext$(); + this.c = ""; + this.closed = true; +}; + +/** + * @param {!string} message + */ +SAXParser.prototype.strictFail$S = function (message) { + if (this.strict) { + this.emiterror$S(message); + } +}; + +/** + */ +SAXParser.prototype.newTag$ = function () { + if (! this.strict) { + this.tagName = this.tagName.toLowerCase(); + } + this.tag = ({name: this.tagName, attributes: ({ }), isSelfClosing: false}); + this.attribList.length = 0; +}; + +/** + */ +SAXParser.prototype.attrib$ = function () { + if (! this.strict) { + this.attribName = this.attribName.toLowerCase(); + } + if ($__jsx_ObjectHasOwnProperty.call(this.tag.attributes, this.attribName)) { + this.attribName = this.attribValue = ""; + return; + } + this.tag.attributes[this.attribName] = this.attribValue; + this.closetext_if_exist$(); + this.attribName = this.attribValue = ""; +}; + +/** + */ +SAXParser.prototype.openTag$ = function () { + this.openTag$B(false); +}; + +/** + * @param {!boolean} selfClosing + */ +SAXParser.prototype.openTag$B = function (selfClosing) { + /** @type {Tag} */ + var tag$0; + /** @type {Tag} */ + var tag$1; + (tag$0 = this.tag).isSelfClosing = selfClosing; + this.sawRoot = true; + this.tags.push(tag$0); + this.closetext_if_exist$(); + this.handler.onopentag$SHS((tag$1 = this.tag).name, tag$1.attributes); + if (this.tag.name === 'pre') { + this.preTags++; + } + if (! selfClosing) { + if (! this.noscript && this.tagName.toLowerCase() === "script") { + this.state = 33; + } else { + this.state = 2; + } + this.tag = null; + this.tagName = ""; + } + this.attribName = this.attribValue = ""; + this.attribList.length = 0; +}; + +/** + */ +SAXParser.prototype.closeTag$ = function () { + /** @type {!number} */ + var t; + /** @type {!string} */ + var tagName; + /** @type {!string} */ + var closeTo; + /** @type {Tag} */ + var close; + /** @type {!number} */ + var s; + /** @type {Tag} */ + var tag$0; + if (! this.tagName) { + this.strictFail$S("Weird empty close tag."); + this.textNode += ""; + this.state = 2; + return; + } + if (this.script) { + if (this.tagName !== "script") { + this.script += ""; + this.tagName = ""; + this.state = 33; + return; + } + this.closetext_if_exist$(); + this.script = ""; + } + t = this.tags.length; + tagName = this.tagName; + if (! this.strict) { + tagName = tagName.toLowerCase(); + } + closeTo = tagName; + while (t--) { + close = this.tags[t]; + if (close.name !== closeTo) { + this.strictFail$S("Unexpected close tag"); + } else { + break; + } + } + if (t < 0) { + this.strictFail$S("Unmatched closing tag: " + this.tagName); + this.textNode += ""; + this.state = 2; + return; + } + this.tagName = tagName; + s = this.tags.length; + while (s-- > t) { + tag$0 = this.tag = this.tags.pop(); + this.tagName = tag$0.name; + this.closetext_if_exist$(); + this.handler.onclosetag$S(this.tagName); + if (this.tagName === 'pre') { + this.preTags--; + } + } + if (t === 0) { + this.closedRoot = true; + } + this.tagName = this.attribValue = this.attribName = ""; + this.attribList.length = 0; + this.state = 2; +}; + +/** + * @return {!string} + */ +SAXParser.prototype.parseEntity$ = function () { + /** @type {!string} */ + var entity; + /** @type {!string} */ + var entityLC; + /** @type {!number} */ + var num; + /** @type {!string} */ + var numStr; + entity = this.entity; + entityLC = entity.toLowerCase(); + num = 0; + numStr = ""; + if (this.ENTITIES[entity]) { + return this.ENTITIES[entity]; + } + if (this.ENTITIES[entityLC]) { + return this.ENTITIES[entityLC]; + } + entity = entityLC; + if (entityLC.charAt(0) === "#") { + if (entity.charAt(1) === "x") { + entity = entity.slice(2); + num = $__jsx_parseInt(entity, 16); + numStr = num.toString(16); + } else { + entity = entity.slice(1); + num = $__jsx_parseInt(entity, 10); + numStr = num.toString(10); + } + } + entity = entity.replace(/^0+/, ""); + if (numStr.toLowerCase() !== entity) { + this.strictFail$S("Invalid character entity"); + return "&" + this.entity + ";"; + } + return String.fromCharCode(num); +}; + +/** + * class Char extends Object + * @constructor + */ +function Char() { +} + +/** + * @constructor + */ +function Char$() { + this.CDATA = "[CDATA["; + this.DOCTYPE = "DOCTYPE"; + this.XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace"; + this.whitespace = this._charClass$S("\r\n\t "); + this.number = this._charClass$S("0124356789"); + this.letter = this._charClass$S("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + this.quote = this._charClass$S("'\""); + this.entity = this._charClass$S("0124356789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#"); + this.attribEnd = this._charClass$S("\r\n\t >"); + this.nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/; + this.nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040\.\d-]/; +}; + +Char$.prototype = new Char; + +/** + * @param {!string} str + * @return {Object.} + */ +Char.prototype._charClass$S = function (str) { + /** @type {Object.} */ + var result; + /** @type {!number} */ + var i; + result = ({ }); + for (i = 0; i < str.length; i++) { + result[str.slice(i, i + 1)] = true; + } + return result; +}; + +/** + * @param {RegExp} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.is$LRegExp$S = function (charclass, c) { + return charclass.test(c); +}; + +/** + * @param {Object.} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.is$HBS = function (charclass, c) { + return $__jsx_ObjectHasOwnProperty.call(charclass, c); +}; + +/** + * @param {RegExp} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.not$LRegExp$S = function (charclass, c) { + return ! charclass.test(c); +}; + +/** + * @param {Object.} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.not$HBS = function (charclass, c) { + return ! $__jsx_ObjectHasOwnProperty.call(charclass, c); +}; + +/** + * class _Entities extends Object + * @constructor + */ +function _Entities() { +} + +/** + * @constructor + */ +function _Entities$() { +}; + +_Entities$.prototype = new _Entities; + +/** + * @return {Object.} + */ +_Entities.entity_list$ = function () { + /** @type {Object.} */ + var result; + /** @type {!string} */ + var key; + /** @type {*} */ + var value; + result = ({ }); + for (key in _Entities._entities) { + value = _Entities._entities[key]; + if (typeof value === 'string') { + result[key] = value + ""; + } else { + if (typeof value === 'number') { + result[key] = String.fromCharCode(value | 0); + } + } + } + return result; +}; + +var _Entities$entity_list$ = _Entities.entity_list$; + +/** + * class BitVector extends Object + * @constructor + */ +function BitVector() { +} + +/** + * @constructor + */ +function BitVector$() { + /** @type {Array.} */ + var _v$0; + /** @type {Array.} */ + var _r$0; + _r$0 = this._r = [ ]; + _v$0 = this._v = [ ]; + _v$0.length = 0; + _r$0.length = 0; + this._size = 0; + this._size1 = 0; +}; + +BitVector$.prototype = new BitVector; + +/** + */ +BitVector.prototype.build$ = function () { + /** @type {!number} */ + var i; + this._size1 = 0; + for (i = 0; i < this._v.length; i++) { + if (i % 8 === 0) { + this._r.push(true ? this._size1 : this._size - this._size1); + } + this._size1 += this._rank32$IIB(this._v[i], 32, true); + } +}; + +/** + */ +BitVector.prototype.clear$ = function () { + this._v.length = 0; + this._r.length = 0; + this._size = 0; + this._size1 = 0; +}; + +/** + * @return {!number} + */ +BitVector.prototype.size$ = function () { + return this._size; +}; + +/** + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype.size$B = function (b) { + return (b ? this._size1 : this._size - this._size1); +}; + +/** + * @param {!number} value + */ +BitVector.prototype.set$I = function (value) { + this.set$IB(value, true); +}; + +/** + * @param {!number} value + * @param {!boolean} flag + */ +BitVector.prototype.set$IB = function (value, flag) { + /** @type {!number} */ + var q; + /** @type {!number} */ + var r; + /** @type {!number} */ + var m; + if (value >= this._size) { + this._size = (value + 1 | 0); + } + q = (value / 32 | 0); + r = (value % 32 | 0); + while (q >= this._v.length) { + this._v.push(0); + } + m = 0x1 << r; + if (flag) { + this._v[q] |= m; + } else { + this._v[q] &= ~ m; + } +}; + +/** + * @param {!number} value + * @return {!boolean} + */ +BitVector.prototype.get$I = function (value) { + /** @type {!number} */ + var q; + /** @type {!number} */ + var r; + /** @type {!number} */ + var m; + if (value >= this._size) { + throw new Error("BitVector.get() : range error"); + } + q = (value / 32 | 0); + r = (value % 32 | 0); + m = 0x1 << r; + return !! (this._v[q] & m); +}; + +/** + * @param {!number} i + * @return {!number} + */ +BitVector.prototype.rank$I = function (i) { + return this.rank$IB(i, true); +}; + +/** + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype.rank$IB = function (i, b) { + /** @type {!number} */ + var q_large; + /** @type {!number} */ + var q_small; + /** @type {!number} */ + var r; + /** @type {!number} */ + var rank; + /** @type {!number} */ + var begin; + /** @type {!number} */ + var j; + if (i > this._size) { + throw new Error("BitVector.rank() : range error"); + } + if (i === 0) { + return 0; + } + i--; + q_large = (Math.floor(i / 256) | 0); + q_small = (Math.floor(i / 32) | 0); + r = (Math.floor(i % 32) | 0); + rank = (this._r[q_large] | 0); + if (! b) { + rank = q_large * 256 - rank; + } + begin = q_large * 8; + for (j = begin; j < q_small; j++) { + rank += this._rank32$IIB(this._v[j], 32, b); + } + rank += this._rank32$IIB(this._v[q_small], r + 1, b); + return rank; +}; + +/** + * @param {!number} i + * @return {!number} + */ +BitVector.prototype.select$I = function (i) { + return this.select$IB(i, true); +}; + +/** + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype.select$IB = function (i, b) { + /** @type {!number} */ + var left; + /** @type {!number} */ + var right; + /** @type {!number} */ + var pivot; + /** @type {undefined|!number} */ + var rank; + /** @type {!number} */ + var j; + if (i >= (b ? this._size1 : this._size - this._size1)) { + throw new Error("BitVector.select() : range error"); + } + left = 0; + right = this._r.length; + while (left < right) { + pivot = Math.floor((left + right) / 2); + rank = this._r[pivot]; + if (! b) { + rank = pivot * 256 - rank; + } + if (i < rank) { + right = pivot; + } else { + left = pivot + 1; + } + } + right--; + if (b) { + i -= (this._r[right] | 0); + } else { + i -= (right * 256 - this._r[right] | 0); + } + j = right * 8; + while (1) { + rank = this._rank32$IIB(this._v[j], 32, b); + if (i < rank) { + break; + } + j++; + i -= (rank | 0); + } + return (j * 32 + this._select32$IIB(this._v[j], i, b) | 0); +}; + +/** + * @param {!number} x + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype._rank32$IIB = function (x, i, b) { + if (! b) { + x = ~ x; + } + x <<= 32 - i; + x = ((x & 0xaaaaaaaa) >>> 1) + (x & 0x55555555); + x = ((x & 0xcccccccc) >>> 2) + (x & 0x33333333); + x = ((x & 0xf0f0f0f0) >>> 4) + (x & 0x0f0f0f0f); + x = ((x & 0xff00ff00) >>> 8) + (x & 0x00ff00ff); + x = ((x & 0xffff0000) >>> 16) + (x & 0x0000ffff); + return x; +}; + +/** + * @param {!number} x + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype._select32$IIB = function (x, i, b) { + /** @type {!number} */ + var x1; + /** @type {!number} */ + var x2; + /** @type {!number} */ + var x3; + /** @type {!number} */ + var x4; + /** @type {!number} */ + var x5; + /** @type {!number} */ + var pos; + /** @type {!number} */ + var v5; + /** @type {!number} */ + var v4; + /** @type {!number} */ + var v3; + /** @type {!number} */ + var v2; + /** @type {!number} */ + var v1; + /** @type {!number} */ + var v0; + if (! b) { + x = ~ x; + } + x1 = ((x & 0xaaaaaaaa) >>> 1) + (x & 0x55555555); + x2 = ((x1 & 0xcccccccc) >>> 2) + (x1 & 0x33333333); + x3 = ((x2 & 0xf0f0f0f0) >>> 4) + (x2 & 0x0f0f0f0f); + x4 = ((x3 & 0xff00ff00) >>> 8) + (x3 & 0x00ff00ff); + x5 = ((x4 & 0xffff0000) >>> 16) + (x4 & 0x0000ffff); + i++; + pos = 0; + v5 = x5 & 0xffffffff; + if (i > v5) { + i -= (v5 | 0); + pos += 32; + } + v4 = x4 >>> pos & 0x0000ffff; + if (i > v4) { + i -= (v4 | 0); + pos += 16; + } + v3 = x3 >>> pos & 0x000000ff; + if (i > v3) { + i -= (v3 | 0); + pos += 8; + } + v2 = x2 >>> pos & 0x0000000f; + if (i > v2) { + i -= (v2 | 0); + pos += 4; + } + v1 = x1 >>> pos & 0x00000003; + if (i > v1) { + i -= (v1 | 0); + pos += 2; + } + v0 = x >>> pos & 0x00000001; + if (i > v0) { + i -= (v0 | 0); + pos += 1; + } + return (pos | 0); +}; + +/** + * @return {!string} + */ +BitVector.prototype.dump$ = function () { + /** @type {Array.} */ + var contents; + contents = [ ]; + contents.push(Binary$dump32bitNumber$N(this._size)); + contents.push(Binary$dump32bitNumberList$AN(this._v)); + return contents.join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +BitVector.prototype.dump$LCompressionReport$ = function (report) { + /** @type {Array.} */ + var contents; + contents = [ ]; + contents.push(Binary$dump32bitNumber$N(this._size)); + CompressionReport$add$LCompressionReport$II(report, 2, 2); + contents.push(Binary$dump32bitNumberList$ANLCompressionReport$(this._v, report)); + return contents.join(''); +}; + +/** + * @param {!string} data + * @return {!number} + */ +BitVector.prototype.load$S = function (data) { + return this.load$SI(data, 0); +}; + +/** + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +BitVector.prototype.load$SI = function (data, offset) { + /** @type {LoadedNumberListResult} */ + var result; + /** @type {!number} */ + var result$0; + this._v.length = 0; + this._r.length = 0; + this._size = 0; + this._size1 = 0; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + this._size = (result$0 | 0); + result = Binary$load32bitNumberList$SI(data, offset + 2); + this._v = result.result; + this.build$(); + return result.offset; +}; + +/** + * class WaveletMatrix extends Object + * @constructor + */ +function WaveletMatrix() { +} + +/** + * @constructor + */ +function WaveletMatrix$() { + /** @type {Array.} */ + var _bv$0; + /** @type {Array.} */ + var _seps$0; + this._range = ({ }); + _bv$0 = this._bv = [ ]; + _seps$0 = this._seps = [ ]; + this._bitsize = 16; + _bv$0.length = 0; + _seps$0.length = 0; + this._size = 0; +}; + +WaveletMatrix$.prototype = new WaveletMatrix; + +/** + * @return {!number} + */ +WaveletMatrix.prototype.bitsize$ = function () { + return this._bitsize; +}; + +/** + * @param {!number} charCode + */ +WaveletMatrix.prototype.setMaxCharCode$I = function (charCode) { + this._bitsize = (Math.ceil(Math.log(charCode) / 0.6931471805599453) | 0); +}; + +/** + */ +WaveletMatrix.prototype.clear$ = function () { + this._bv.length = 0; + this._seps.length = 0; + this._size = 0; +}; + +/** + * @param {!string} v + */ +WaveletMatrix.prototype.build$S = function (v) { + /** @type {!number} */ + var size; + /** @type {!number} */ + var bitsize; + /** @type {!number} */ + var i; + /** @type {!number} */ + var depth; + /** @type {Object.} */ + var range_tmp; + /** @type {!number} */ + var code; + /** @type {!boolean} */ + var bit; + /** @type {!number} */ + var key; + /** @type {Object.} */ + var range_rev; + /** @type {!string} */ + var range_key; + /** @type {!number} */ + var value; + /** @type {!number} */ + var pos0; + /** @type {undefined|!number} */ + var pos1; + /** @type {!string} */ + var range_rev_key; + /** @type {!number} */ + var begin; + /** @type {undefined|!number} */ + var end; + /** @type {!number} */ + var num0; + /** @type {!number} */ + var num1; + this._bv.length = 0; + this._seps.length = 0; + this._size = 0; + size = v.length; + bitsize = this._bitsize; + for (i = 0; i < bitsize; i++) { + this._bv.push(new BitVector$()); + this._seps.push(0); + } + this._size = (size | 0); + for (i = 0; i < size; i++) { + this._bv[0].set$IB(i, this._uint2bit$II(v.charCodeAt(i), 0)); + } + this._bv[0].build$(); + this._seps[0] = this._bv[0].size$B(false); + this._range["0"] = 0; + this._range["1"] = this._seps[0]; + depth = 1; + while (depth < bitsize) { + range_tmp = WaveletMatrix$_shallow_copy$HI(this._range); + for (i = 0; i < size; i++) { + code = v.charCodeAt(i); + bit = this._uint2bit$II(code, depth); + key = code >>> bitsize - depth; + this._bv[depth].set$IB(range_tmp[key + ""], bit); + range_tmp[key + ""]++; + } + this._bv[depth].build$(); + this._seps[depth] = this._bv[depth].size$B(false); + range_rev = ({ }); + for (range_key in this._range) { + value = this._range[range_key]; + if (value != range_tmp[range_key]) { + range_rev[value + ""] = range_key | 0; + } + } + this._range = ({ }); + pos0 = 0; + pos1 = this._seps[depth]; + for (range_rev_key in range_rev) { + begin = range_rev_key | 0; + value = range_rev[range_rev_key]; + end = range_tmp[value + ""]; + num0 = this._bv[depth].rank$IB(end, false) - this._bv[depth].rank$IB(begin, false); + num1 = end - begin - num0; + if (num0 > 0) { + this._range[(value << 1) + ""] = (pos0 | 0); + pos0 += num0; + } + if (num1 > 0) { + this._range[(value << 1) + 1 + ""] = pos1; + pos1 += (num1 | 0); + } + } + depth++; + } +}; + +/** + * @return {!number} + */ +WaveletMatrix.prototype.size$ = function () { + return this._size; +}; + +/** + * @param {!number} c + * @return {!number} + */ +WaveletMatrix.prototype.size$I = function (c) { + return this.rank$II(this._size, c); +}; + +/** + * @param {!number} i + * @return {!number} + */ +WaveletMatrix.prototype.get$I = function (i) { + /** @type {!number} */ + var value; + /** @type {!number} */ + var depth; + /** @type {!boolean} */ + var bit; + if (i >= this._size) { + throw new Error("WaveletMatrix.get() : range error"); + } + value = 0; + depth = 0; + while (depth < this._bitsize) { + bit = this._bv[depth].get$I(i); + i = this._bv[depth].rank$IB(i, bit); + value <<= 1; + if (bit) { + i += this._seps[depth]; + value += 1; + } + depth++; + } + return (value | 0); +}; + +/** + * @param {!number} i + * @param {!number} c + * @return {!number} + */ +WaveletMatrix.prototype.rank$II = function (i, c) { + /** @type {undefined|!number} */ + var begin; + /** @type {!number} */ + var end; + /** @type {!number} */ + var depth; + /** @type {!boolean} */ + var bit; + if (i > this._size) { + throw new Error("WaveletMatrix.rank(): range error"); + } + if (i === 0) { + return 0; + } + begin = this._range[c + ""]; + if (begin == null) { + return 0; + } + end = i; + depth = 0; + while (depth < this._bitsize) { + bit = this._uint2bit$II(c, depth); + end = this._bv[depth].rank$IB(end, bit); + if (bit) { + end += this._seps[depth]; + } + depth++; + } + return (end - begin | 0); +}; + +/** + * @param {!number} i + * @param {!number} c + * @return {!number} + */ +WaveletMatrix.prototype.rank_less_than$II = function (i, c) { + /** @type {!number} */ + var begin; + /** @type {!number} */ + var end; + /** @type {!number} */ + var depth; + /** @type {!number} */ + var rlt; + /** @type {!number} */ + var rank0_begin; + /** @type {!number} */ + var rank0_end; + /** @type {Array.} */ + var _seps$0; + if (i > this._size) { + throw new Error("WaveletMatrix.rank_less_than(): range error"); + } + if (i === 0) { + return 0; + } + begin = 0; + end = i; + depth = 0; + rlt = 0; + while (depth < this._bitsize) { + rank0_begin = this._bv[depth].rank$IB(begin, false); + rank0_end = this._bv[depth].rank$IB(end, false); + if (this._uint2bit$II(c, depth)) { + rlt += rank0_end - rank0_begin; + begin += (_seps$0 = this._seps)[depth] - rank0_begin; + end += _seps$0[depth] - rank0_end; + } else { + begin = rank0_begin; + end = rank0_end; + } + depth++; + } + return (rlt | 0); +}; + +/** + * @return {!string} + */ +WaveletMatrix.prototype.dump$ = function () { + /** @type {Array.} */ + var contents; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var range_contents; + /** @type {!number} */ + var counter; + /** @type {!string} */ + var key; + contents = [ Binary$dump16bitNumber$I(this._bitsize), Binary$dump32bitNumber$N(this._size) ]; + for (i = 0; i < this._bitsize; i++) { + contents.push(this._bv[i].dump$()); + } + for (i = 0; i < this._bitsize; i++) { + contents.push(Binary$dump32bitNumber$N(this._seps[i])); + } + range_contents = [ ]; + counter = 0; + for (key in this._range) { + range_contents.push(Binary$dump32bitNumber$N(key | 0)); + range_contents.push(Binary$dump32bitNumber$N(this._range[key])); + counter++; + } + contents.push(Binary$dump32bitNumber$N(counter)); + return contents.join('') + range_contents.join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +WaveletMatrix.prototype.dump$LCompressionReport$ = function (report) { + /** @type {Array.} */ + var contents; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var range_contents; + /** @type {!number} */ + var counter; + /** @type {!string} */ + var key; + contents = [ Binary$dump16bitNumber$I(this._bitsize), Binary$dump32bitNumber$N(this._size) ]; + CompressionReport$add$LCompressionReport$II(report, 3, 3); + for (i = 0; i < this._bitsize; i++) { + contents.push(this._bv[i].dump$LCompressionReport$(report)); + } + for (i = 0; i < this._bitsize; i++) { + contents.push(Binary$dump32bitNumber$N(this._seps[i])); + CompressionReport$add$LCompressionReport$II(report, 2, 2); + } + range_contents = [ ]; + counter = 0; + for (key in this._range) { + range_contents.push(Binary$dump32bitNumber$N(key | 0)); + range_contents.push(Binary$dump32bitNumber$N(this._range[key])); + CompressionReport$add$LCompressionReport$II(report, 4, 4); + counter++; + } + CompressionReport$add$LCompressionReport$II(report, 2, 2); + contents.push(Binary$dump32bitNumber$N(counter)); + return contents.join('') + range_contents.join(''); +}; + +/** + * @param {!string} data + * @return {!number} + */ +WaveletMatrix.prototype.load$S = function (data) { + return this.load$SI(data, 0); +}; + +/** + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +WaveletMatrix.prototype.load$SI = function (data, offset) { + /** @type {!number} */ + var i; + /** @type {BitVector} */ + var bit_vector; + /** @type {!number} */ + var range_size; + /** @type {!number} */ + var value; + /** @type {!number} */ + var offset$0; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var result$1; + /** @type {!number} */ + var result$2; + this._bv.length = 0; + this._seps.length = 0; + this._size = 0; + offset$0 = offset++; + this._bitsize = (data.charCodeAt(offset$0) | 0); + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + this._size = (result$0 | 0); + offset += 2; + for (i = 0; i < this._bitsize; i++) { + bit_vector = new BitVector$(); + offset = bit_vector.load$SI(data, offset); + this._bv.push(bit_vector); + } + for (i = 0; i < this._bitsize; (i++, offset += 2)) { + this._seps.push(Binary$load32bitNumber$SI(data, offset)); + } + result$1 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + range_size = result$1; + offset += 2; + for (i = 0; i < range_size; (i++, offset += 4)) { + result$2 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + value = Binary$load32bitNumber$SI(data, offset + 2); + this._range[result$2 + ""] = (value | 0); + } + return offset; +}; + +/** + * @param {Object.} input + * @return {Object.} + */ +WaveletMatrix._shallow_copy$HI = function (input) { + /** @type {Object.} */ + var result; + /** @type {!string} */ + var key; + result = ({ }); + for (key in input) { + result[key] = input[key]; + } + return result; +}; + +var WaveletMatrix$_shallow_copy$HI = WaveletMatrix._shallow_copy$HI; + +/** + * @param {!number} c + * @param {!number} i + * @return {!boolean} + */ +WaveletMatrix.prototype._uint2bit$II = function (c, i) { + return (c >>> this._bitsize - 1 - i & 0x1) === 0x1; +}; + +/** + * class BurrowsWheelerTransform extends Object + * @constructor + */ +function BurrowsWheelerTransform() { +} + +/** + * @constructor + */ +function BurrowsWheelerTransform$() { + this._str = ""; + this._size = 0; + this._head = 0; + this._suffixarray = [ ]; +}; + +BurrowsWheelerTransform$.prototype = new BurrowsWheelerTransform; + +/** + * @param {BurrowsWheelerTransform} $this + * @return {!number} + */ +BurrowsWheelerTransform.size$LBurrowsWheelerTransform$ = function ($this) { + return $this._size; +}; + +var BurrowsWheelerTransform$size$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.size$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + * @return {!number} + */ +BurrowsWheelerTransform.head$LBurrowsWheelerTransform$ = function ($this) { + return $this._head; +}; + +var BurrowsWheelerTransform$head$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.head$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + */ +BurrowsWheelerTransform.clear$LBurrowsWheelerTransform$ = function ($this) { + $this._str = ""; + $this._size = 0; + $this._head = 0; + $this._suffixarray.length = 0; +}; + +var BurrowsWheelerTransform$clear$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.clear$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + * @param {!string} str + */ +BurrowsWheelerTransform.build$LBurrowsWheelerTransform$S = function ($this, str) { + /** @type {!string} */ + var _str$0; + /** @type {Array.} */ + var _suffixarray$0; + _str$0 = $this._str = str; + $this._size = _str$0.length; + _suffixarray$0 = $this._suffixarray = SAIS$make$S(str); + $this._head = (_suffixarray$0.indexOf(0) | 0); +}; + +var BurrowsWheelerTransform$build$LBurrowsWheelerTransform$S = BurrowsWheelerTransform.build$LBurrowsWheelerTransform$S; + +/** + * @param {BurrowsWheelerTransform} $this + * @param {!number} i + * @return {!string} + */ +BurrowsWheelerTransform.get$LBurrowsWheelerTransform$I = function ($this, i) { + /** @type {!number} */ + var size; + /** @type {!number} */ + var index; + size = $this._size; + if (i >= size) { + throw new Error("BurrowsWheelerTransform.get() : range error"); + } + index = ($this._suffixarray[i] + size - 1) % size; + return $this._str.charAt(index); +}; + +var BurrowsWheelerTransform$get$LBurrowsWheelerTransform$I = BurrowsWheelerTransform.get$LBurrowsWheelerTransform$I; + +/** + * @param {BurrowsWheelerTransform} $this + * @return {!string} + */ +BurrowsWheelerTransform.get$LBurrowsWheelerTransform$ = function ($this) { + /** @type {Array.} */ + var str; + /** @type {!number} */ + var size; + /** @type {!number} */ + var i; + str = [ ]; + size = $this._size; + for (i = 0; i < size; i++) { + str.push(BurrowsWheelerTransform$get$LBurrowsWheelerTransform$I($this, i)); + } + return str.join(""); +}; + +var BurrowsWheelerTransform$get$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.get$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + * @param {!string} replace + * @return {!string} + */ +BurrowsWheelerTransform.get$LBurrowsWheelerTransform$S = function ($this, replace) { + /** @type {!string} */ + var result; + result = BurrowsWheelerTransform$get$LBurrowsWheelerTransform$($this); + return result.replace(BurrowsWheelerTransform.END_MARKER, replace); +}; + +var BurrowsWheelerTransform$get$LBurrowsWheelerTransform$S = BurrowsWheelerTransform.get$LBurrowsWheelerTransform$S; + +/** + * class OArray extends Object + * @constructor + */ +function OArray() { +} + +/** + * @constructor + * @param {Array.} array + */ +function OArray$AI(array) { + this.array = array; + this.offset = 0; +}; + +OArray$AI.prototype = new OArray; + +/** + * @constructor + * @param {Array.} array + * @param {!number} offset + */ +function OArray$AII(array, offset) { + this.array = array; + this.offset = offset; +}; + +OArray$AII.prototype = new OArray; + +/** + * @param {OArray} $this + * @param {!number} index + * @return {!number} + */ +OArray.get$LOArray$I = function ($this, index) { + return $this.array[index + $this.offset]; +}; + +var OArray$get$LOArray$I = OArray.get$LOArray$I; + +/** + * @param {OArray} $this + * @param {!number} index + * @param {!number} value + */ +OArray.set$LOArray$II = function ($this, index, value) { + $this.array[index + $this.offset] = value; +}; + +var OArray$set$LOArray$II = OArray.set$LOArray$II; + +/** + * @param {OArray} $this + * @param {!number} index + * @return {!boolean} + */ +OArray.isS$LOArray$I = function ($this, index) { + /** @type {Array.} */ + var array$0; + /** @type {!number} */ + var offset$0; + return (array$0 = $this.array)[index + (offset$0 = $this.offset)] < array$0[index + offset$0 + 1]; +}; + +var OArray$isS$LOArray$I = OArray.isS$LOArray$I; + +/** + * @param {OArray} $this + * @param {!number} index1 + * @param {!number} index2 + * @return {!boolean} + */ +OArray.compare$LOArray$II = function ($this, index1, index2) { + /** @type {Array.} */ + var array$0; + /** @type {!number} */ + var offset$0; + return (array$0 = $this.array)[index1 + (offset$0 = $this.offset)] == array$0[index2 + offset$0]; +}; + +var OArray$compare$LOArray$II = OArray.compare$LOArray$II; + +/** + * class SAIS extends Object + * @constructor + */ +function SAIS() { +} + +/** + * @constructor + */ +function SAIS$() { +}; + +SAIS$.prototype = new SAIS; + +/** + * @param {BitVector} t + * @param {!number} i + * @return {!boolean} + */ +SAIS._isLMS$LBitVector$I = function (t, i) { + return i > 0 && t.get$I(i) && ! t.get$I(i - 1); +}; + +var SAIS$_isLMS$LBitVector$I = SAIS._isLMS$LBitVector$I; + +/** + * @param {OArray} s + * @param {Array.} bkt + * @param {!number} n + * @param {!number} K + * @param {!boolean} end + */ +SAIS._getBuckets$LOArray$AIIIB = function (s, bkt, n, K, end) { + /** @type {!number} */ + var sum; + /** @type {!number} */ + var i; + sum = 0; + for (i = 0; i <= K; i++) { + bkt[i] = 0; + } + for (i = 0; i < n; i++) { + bkt[OArray$get$LOArray$I(s, i)]++; + } + for (i = 0; i <= K; i++) { + sum += bkt[i]; + bkt[i] = ((end ? sum : sum - bkt[i]) | 0); + } +}; + +var SAIS$_getBuckets$LOArray$AIIIB = SAIS._getBuckets$LOArray$AIIIB; + +/** + * @param {BitVector} t + * @param {Array.} SA + * @param {OArray} s + * @param {Array.} bkt + * @param {!number} n + * @param {!number} K + * @param {!boolean} end + */ +SAIS._induceSAl$LBitVector$AILOArray$AIIIB = function (t, SA, s, bkt, n, K, end) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var j; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, end); + for (i = 0; i < n; i++) { + j = SA[i] - 1; + if (j >= 0 && ! t.get$I(j)) { + SA[bkt[OArray$get$LOArray$I(s, j)]++] = (j | 0); + } + } +}; + +var SAIS$_induceSAl$LBitVector$AILOArray$AIIIB = SAIS._induceSAl$LBitVector$AILOArray$AIIIB; + +/** + * @param {BitVector} t + * @param {Array.} SA + * @param {OArray} s + * @param {Array.} bkt + * @param {!number} n + * @param {!number} K + * @param {!boolean} end + */ +SAIS._induceSAs$LBitVector$AILOArray$AIIIB = function (t, SA, s, bkt, n, K, end) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var j; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, end); + for (i = n - 1; i >= 0; i--) { + j = SA[i] - 1; + if (j >= 0 && t.get$I(j)) { + SA[-- bkt[OArray$get$LOArray$I(s, j)]] = (j | 0); + } + } +}; + +var SAIS$_induceSAs$LBitVector$AILOArray$AIIIB = SAIS._induceSAs$LBitVector$AILOArray$AIIIB; + +/** + * @param {!string} source + * @return {Array.} + */ +SAIS.make$S = function (source) { + /** @type {Array.} */ + var charCodes; + /** @type {!number} */ + var maxCode; + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {Array.} */ + var SA; + /** @type {OArray} */ + var s; + charCodes = [ ]; + charCodes.length = source.length; + maxCode = 0; + for (i = 0; i < source.length; i++) { + code = source.charCodeAt(i); + charCodes[i] = (code | 0); + maxCode = (code > maxCode ? code : maxCode); + } + SA = [ ]; + SA.length = source.length; + s = ({offset: 0, array: charCodes}); + SAIS$_make$LOArray$AIII(s, SA, source.length, maxCode); + return SA; +}; + +var SAIS$make$S = SAIS.make$S; + +/** + * @param {OArray} s + * @param {Array.} SA + * @param {!number} n + * @param {!number} K + */ +SAIS._make$LOArray$AIII = function (s, SA, n, K) { + /** @type {BitVector} */ + var t; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var bkt; + /** @type {!number} */ + var n1; + /** @type {!number} */ + var name; + /** @type {!number} */ + var prev; + /** @type {undefined|!number} */ + var pos; + /** @type {!boolean} */ + var diff; + /** @type {!number} */ + var d; + /** @type {!number} */ + var j; + /** @type {Array.} */ + var SA1; + /** @type {OArray} */ + var s1; + /** @type {!number} */ + var i$0; + /** @type {!number} */ + var index$0; + t = new BitVector$(); + t.set$IB(n - 2, false); + t.set$IB(n - 1, true); + for (i = n - 3; i >= 0; i--) { + t.set$IB(i, OArray$isS$LOArray$I(s, i) || OArray$compare$LOArray$II(s, i, i + 1) && t.get$I(i + 1)); + } + bkt = [ ]; + bkt.length = K + 1; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, true); + for (i = 0; i < n; i++) { + SA[i] = -1; + } + for (i = 1; i < n; i++) { + if (SAIS$_isLMS$LBitVector$I(t, i)) { + SA[-- bkt[OArray$get$LOArray$I(s, i)]] = (i | 0); + } + } + SAIS$_induceSAl$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, false); + SAIS$_induceSAs$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, true); + n1 = 0; + for (i = 0; i < n; i++) { + i$0 = SA[i]; + if (i$0 > 0 && t.get$I(i$0) && ! t.get$I(i$0 - 1)) { + SA[n1++] = SA[i]; + } + } + for (i = n1; i < n; i++) { + SA[i] = -1; + } + name = 0; + prev = -1; + for (i = 0; i < n1; i++) { + pos = SA[i]; + diff = false; + for (d = 0; d < n; d++) { + if (prev === -1 || ! OArray$compare$LOArray$II(s, pos + d, prev + d) || t.get$I(pos + d) !== t.get$I(prev + d)) { + diff = true; + break; + } else { + if (d > 0 && (SAIS$_isLMS$LBitVector$I(t, pos + d) || SAIS$_isLMS$LBitVector$I(t, prev + d))) { + break; + } + } + } + if (diff) { + name++; + prev = pos; + } + pos = ((pos % 2 === 0 ? pos / 2 : (pos - 1) / 2) | 0); + SA[n1 + pos] = (name - 1 | 0); + } + for ((i = n - 1, j = n - 1); i >= n1; i--) { + if (SA[i] >= 0) { + SA[j--] = SA[i]; + } + } + SA1 = SA; + s1 = ({offset: n - n1, array: SA}); + if (name < n1) { + SAIS$_make$LOArray$AIII(s1, SA1, n1, name - 1); + } else { + for (i = 0; i < n1; i++) { + SA1[OArray$get$LOArray$I(s1, i)] = (i | 0); + } + } + bkt = [ ]; + bkt.length = K + 1; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, true); + for ((i = 1, j = 0); i < n; i++) { + if (SAIS$_isLMS$LBitVector$I(t, i)) { + OArray$set$LOArray$II(s1, j++, i); + } + } + for (i = 0; i < n1; i++) { + index$0 = SA1[i]; + SA1[i] = s1.array[index$0 + s1.offset]; + } + for (i = n1; i < n; i++) { + SA[i] = -1; + } + for (i = n1 - 1; i >= 0; i--) { + j = SA[i]; + SA[i] = -1; + SA[-- bkt[OArray$get$LOArray$I(s, j)]] = (j | 0); + } + SAIS$_induceSAl$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, false); + SAIS$_induceSAs$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, true); +}; + +var SAIS$_make$LOArray$AIII = SAIS._make$LOArray$AIII; + +OktaviaSearch._stemmer = null; +OktaviaSearch._instance = null; +$__jsx_lazy_init(Oktavia, "eof", function () { + return String.fromCharCode(0); +}); +$__jsx_lazy_init(Oktavia, "eob", function () { + return String.fromCharCode(1); +}); +$__jsx_lazy_init(Oktavia, "unknown", function () { + return String.fromCharCode(3); +}); +Binary._base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +$__jsx_lazy_init(Binary, "_base64DecodeChars", function () { + return [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 ]; +}); +$__jsx_lazy_init(Style, "console", function () { + return ({ 'title': [ '\x1B[32m\x1b[4m', '\x1B[39m\x1b[0m' ], 'url': [ '\x1B[34m', '\x1B[39m' ], 'hit': [ '\x1B[4m', '\x1B[0m' ], 'del': [ '\x1B[9m', '\x1B[0m' ], 'summary': [ '\x1B[90m', '\x1B[39m' ] }); +}); +$__jsx_lazy_init(Style, "html", function () { + return ({ 'title': [ '', '' ], 'url': [ '', '' ], 'hit': [ '', '' ], 'del': [ '', '' ], 'summary': [ '', '' ] }); +}); +$__jsx_lazy_init(Style, "ignore", function () { + return ({ 'tilte': [ '', '' ], 'url': [ '', '' ], 'hit': [ '', '' ], 'del': [ '', '' ], 'summary': [ '', '' ] }); +}); +$__jsx_lazy_init(_Common, "buffers", function () { + return [ "comment", "sgmlDecl", "textNode", "tagName", "doctype", "procInstName", "procInstBody", "entity", "attribName", "attribValue", "cdata", "script" ]; +}); +$__jsx_lazy_init(_Common, "EVENTS", function () { + return [ "text", "processinginstruction", "sgmldeclaration", "doctype", "comment", "attribute", "opentag", "closetag", "opencdata", "cdata", "clo_State.CDATA", "error", "end", "ready", "script", "opennamespace", "closenamespace" ]; +}); +_Common.MAX_BUFFER_LENGTH = 65536; +_State.BEGIN = 1; +_State.TEXT = 2; +_State.TEXT_ENTITY = 3; +_State.OPEN_WAKA = 4; +_State.SGML_DECL = 5; +_State.SGML_DECL_QUOTED = 6; +_State.DOCTYPE = 7; +_State.DOCTYPE_QUOTED = 8; +_State.DOCTYPE_DTD = 9; +_State.DOCTYPE_DTD_QUOTED = 10; +_State.COMMENT_STARTING = 11; +_State.COMMENT = 12; +_State.COMMENT_ENDING = 13; +_State.COMMENT_ENDED = 14; +_State.CDATA = 15; +_State.CDATA_ENDING = 16; +_State.CDATA_ENDING_2 = 17; +_State.PROC_INST = 18; +_State.PROC_INST_BODY = 19; +_State.PROC_INST_ENDING = 20; +_State.OPEN_TAG = 21; +_State.OPEN_TAG_SLASH = 22; +_State.ATTRIB = 23; +_State.ATTRIB_NAME = 24; +_State.ATTRIB_NAME_SAW_WHITE = 25; +_State.ATTRIB_VALUE = 26; +_State.ATTRIB_VALUE_QUOTED = 27; +_State.ATTRIB_VALUE_UNQUOTED = 28; +_State.ATTRIB_VALUE_ENTITY_Q = 29; +_State.ATTRIB_VALUE_ENTITY_U = 30; +_State.CLOSE_TAG = 31; +_State.CLOSE_TAG_SAW_WHITE = 32; +_State.SCRIPT = 33; +_State.SCRIPT_ENDING = 34; +$__jsx_lazy_init(_Entities, "_entities", function () { + return ({ "amp": "&", "gt": ">", "lt": "<", "quot": "\"", "apos": "'", "AElig": 198, "Aacute": 193, "Acirc": 194, "Agrave": 192, "Aring": 197, "Atilde": 195, "Auml": 196, "Ccedil": 199, "ETH": 208, "Eacute": 201, "Ecirc": 202, "Egrave": 200, "Euml": 203, "Iacute": 205, "Icirc": 206, "Igrave": 204, "Iuml": 207, "Ntilde": 209, "Oacute": 211, "Ocirc": 212, "Ograve": 210, "Oslash": 216, "Otilde": 213, "Ouml": 214, "THORN": 222, "Uacute": 218, "Ucirc": 219, "Ugrave": 217, "Uuml": 220, "Yacute": 221, "aacute": 225, "acirc": 226, "aelig": 230, "agrave": 224, "aring": 229, "atilde": 227, "auml": 228, "ccedil": 231, "eacute": 233, "ecirc": 234, "egrave": 232, "eth": 240, "euml": 235, "iacute": 237, "icirc": 238, "igrave": 236, "iuml": 239, "ntilde": 241, "oacute": 243, "ocirc": 244, "ograve": 242, "oslash": 248, "otilde": 245, "ouml": 246, "szlig": 223, "thorn": 254, "uacute": 250, "ucirc": 251, "ugrave": 249, "uuml": 252, "yacute": 253, "yuml": 255, "copy": 169, "reg": 174, "nbsp": 160, "iexcl": 161, "cent": 162, "pound": 163, "curren": 164, "yen": 165, "brvbar": 166, "sect": 167, "uml": 168, "ordf": 170, "laquo": 171, "not": 172, "shy": 173, "macr": 175, "deg": 176, "plusmn": 177, "sup1": 185, "sup2": 178, "sup3": 179, "acute": 180, "micro": 181, "para": 182, "middot": 183, "cedil": 184, "ordm": 186, "raquo": 187, "frac14": 188, "frac12": 189, "frac34": 190, "iquest": 191, "times": 215, "divide": 247, "OElig": 338, "oelig": 339, "Scaron": 352, "scaron": 353, "Yuml": 376, "fnof": 402, "circ": 710, "tilde": 732, "Alpha": 913, "Beta": 914, "Gamma": 915, "Delta": 916, "Epsilon": 917, "Zeta": 918, "Eta": 919, "Theta": 920, "Iota": 921, "Kappa": 922, "Lambda": 923, "Mu": 924, "Nu": 925, "Xi": 926, "Omicron": 927, "Pi": 928, "Rho": 929, "Sigma": 931, "Tau": 932, "Upsilon": 933, "Phi": 934, "Chi": 935, "Psi": 936, "Omega": 937, "alpha": 945, "beta": 946, "gamma": 947, "delta": 948, "epsilon": 949, "zeta": 950, "eta": 951, "theta": 952, "iota": 953, "kappa": 954, "lambda": 955, "mu": 956, "nu": 957, "xi": 958, "omicron": 959, "pi": 960, "rho": 961, "sigmaf": 962, "sigma": 963, "tau": 964, "upsilon": 965, "phi": 966, "chi": 967, "psi": 968, "omega": 969, "thetasym": 977, "upsih": 978, "piv": 982, "ensp": 8194, "emsp": 8195, "thinsp": 8201, "zwnj": 8204, "zwj": 8205, "lrm": 8206, "rlm": 8207, "ndash": 8211, "mdash": 8212, "lsquo": 8216, "rsquo": 8217, "sbquo": 8218, "ldquo": 8220, "rdquo": 8221, "bdquo": 8222, "dagger": 8224, "Dagger": 8225, "bull": 8226, "hellip": 8230, "permil": 8240, "prime": 8242, "Prime": 8243, "lsaquo": 8249, "rsaquo": 8250, "oline": 8254, "frasl": 8260, "euro": 8364, "image": 8465, "weierp": 8472, "real": 8476, "trade": 8482, "alefsym": 8501, "larr": 8592, "uarr": 8593, "rarr": 8594, "darr": 8595, "harr": 8596, "crarr": 8629, "lArr": 8656, "uArr": 8657, "rArr": 8658, "dArr": 8659, "hArr": 8660, "forall": 8704, "part": 8706, "exist": 8707, "empty": 8709, "nabla": 8711, "isin": 8712, "notin": 8713, "ni": 8715, "prod": 8719, "sum": 8721, "minus": 8722, "lowast": 8727, "radic": 8730, "prop": 8733, "infin": 8734, "ang": 8736, "and": 8743, "or": 8744, "cap": 8745, "cup": 8746, "int": 8747, "there4": 8756, "sim": 8764, "cong": 8773, "asymp": 8776, "ne": 8800, "equiv": 8801, "le": 8804, "ge": 8805, "sub": 8834, "sup": 8835, "nsub": 8836, "sube": 8838, "supe": 8839, "oplus": 8853, "otimes": 8855, "perp": 8869, "sdot": 8901, "lceil": 8968, "rceil": 8969, "lfloor": 8970, "rfloor": 8971, "lang": 9001, "rang": 9002, "loz": 9674, "spades": 9824, "clubs": 9827, "hearts": 9829, "diams": 9830 }); +}); +BitVector.SMALL_BLOCK_SIZE = 32; +BitVector.LARGE_BLOCK_SIZE = 256; +BitVector.BLOCK_RATE = 8; +$__jsx_lazy_init(BurrowsWheelerTransform, "END_MARKER", function () { + return String.fromCharCode(0); +}); +var $__jsx_classMap = { + "tool/web/oktavia-search.jsx": { + _Result: _Result, + _Result$SSSI: _Result$SSSI, + _Proposal: _Proposal, + _Proposal$SSI: _Proposal$SSI, + OktaviaSearch: OktaviaSearch, + OktaviaSearch$I: OktaviaSearch$I, + _Main: _Main, + _Main$: _Main$ + }, + "src/oktavia.jsx": { + Oktavia: Oktavia, + Oktavia$: Oktavia$ + }, + "src/binary-util.jsx": { + Binary: Binary, + Binary$: Binary$, + LoadedStringResult: LoadedStringResult, + LoadedStringResult$SI: LoadedStringResult$SI, + LoadedStringListResult: LoadedStringListResult, + LoadedStringListResult$SI: LoadedStringListResult$SI, + LoadedStringListMapResult: LoadedStringListMapResult, + LoadedStringListMapResult$SI: LoadedStringListMapResult$SI, + LoadedNumberListResult: LoadedNumberListResult, + LoadedNumberListResult$SI: LoadedNumberListResult$SI, + CompressionReport: CompressionReport, + CompressionReport$: CompressionReport$ + }, + "src/query.jsx": { + Query: Query, + Query$: Query$ + }, + "src/query-string-parser.jsx": { + QueryStringParser: QueryStringParser, + QueryStringParser$: QueryStringParser$ + }, + "src/search-result.jsx": { + Proposal: Proposal, + Proposal$II: Proposal$II, + Position: Position, + Position$SIB: Position$SIB, + SearchUnit: SearchUnit, + SearchUnit$I: SearchUnit$I, + SingleResult: SingleResult, + SingleResult$: SingleResult$, + SingleResult$SBB: SingleResult$SBB, + SearchSummary: SearchSummary, + SearchSummary$: SearchSummary$, + SearchSummary$LOktavia$: SearchSummary$LOktavia$ + }, + "src/style.jsx": { + Style: Style, + Style$S: Style$S, + _HTMLHandler: _HTMLHandler, + _HTMLHandler$HASB: _HTMLHandler$HASB + }, + "src/stemmer/stemmer.jsx": { + Stemmer: Stemmer, + Stemmer$: Stemmer$ + }, + "src/metadata.jsx": { + Metadata: Metadata, + Metadata$LOktavia$: Metadata$LOktavia$, + Section: Section, + Section$LOktavia$: Section$LOktavia$, + Splitter: Splitter, + Splitter$LOktavia$: Splitter$LOktavia$, + Splitter$LOktavia$S: Splitter$LOktavia$S, + Table: Table, + Table$LOktavia$AS: Table$LOktavia$AS, + Block: Block, + Block$LOktavia$: Block$LOktavia$ + }, + "src/fm-index.jsx": { + FMIndex: FMIndex, + FMIndex$: FMIndex$ + }, + "src/sax.jsx": { + Tag: Tag, + Tag$S: Tag$S, + _Common: _Common, + _Common$: _Common$, + _State: _State, + _State$: _State$, + SAXHandler: SAXHandler, + SAXHandler$: SAXHandler$, + SAXParser: SAXParser, + SAXParser$LSAXHandler$: SAXParser$LSAXHandler$, + SAXParser$LSAXHandler$B: SAXParser$LSAXHandler$B, + Char: Char, + Char$: Char$, + _Entities: _Entities, + _Entities$: _Entities$ + }, + "src/bit-vector.jsx": { + BitVector: BitVector, + BitVector$: BitVector$ + }, + "src/wavelet-matrix.jsx": { + WaveletMatrix: WaveletMatrix, + WaveletMatrix$: WaveletMatrix$ + }, + "src/burrows-wheeler-transform.jsx": { + BurrowsWheelerTransform: BurrowsWheelerTransform, + BurrowsWheelerTransform$: BurrowsWheelerTransform$ + }, + "src/sais.jsx": { + OArray: OArray, + OArray$AI: OArray$AI, + OArray$AII: OArray$AII, + SAIS: SAIS, + SAIS$: SAIS$ + } +}; + + +/** + * launches _Main.main(:string[]):void invoked by jsx --run|--executable + */ +JSX.runMain = function (sourceFile, args) { + var module = JSX.require(sourceFile); + if (! module) { + throw new ReferenceError("entry point module not found in " + sourceFile); + } + if (! module._Main) { + throw new ReferenceError("entry point _Main not found in " + sourceFile); + } + if (! module._Main.main$AS) { + throw new ReferenceError("entry point _Main.main(:string[]):void not found in " + sourceFile); + } + module._Main.main$AS(args); +}; + +/** + * launches _Test#test*():void invoked by jsx --test + */ +JSX.runTests = function (sourceFile, tests) { + var module = JSX.require(sourceFile); + var testClass = module._Test$; + + if (!testClass) return; // skip if there's no test class + + if(tests.length === 0) { + var p = testClass.prototype; + for (var m in p) { + if (p[m] instanceof Function + && /^test.*[$]$/.test(m)) { + tests.push(m); + } + } + } + else { // set as process arguments + tests = tests.map(function (name) { + return name + "$"; // mangle for function test*():void + }); + } + + var testCase = new testClass(); + + if (testCase.beforeClass$AS != null) + testCase.beforeClass$AS(tests); + + for (var i = 0; i < tests.length; ++i) { + (function (method) { + if (method in testCase) { + testCase.run$SF$V$(method, function() { testCase[method](); }); + } + else { + throw new ReferenceError("No such test method: " + method); + } + }(tests[i])); + } + + if (testCase.afterClass$ != null) + testCase.afterClass$(); +}; +/** + * call a function on load/DOMContentLoaded + */ +function $__jsx_onload (event) { + window.removeEventListener("load", $__jsx_onload); + document.removeEventListener("DOMContentLoaded", $__jsx_onload); + JSX.runMain("tool/web/oktavia-search.jsx", []) +} + +window.addEventListener("load", $__jsx_onload); +document.addEventListener("DOMContentLoaded", $__jsx_onload); + +})(JSX); diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/searchstyle.css b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/searchstyle.css new file mode 100644 index 000000000..a3d51d436 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_static/searchstyle.css @@ -0,0 +1,174 @@ +form.oktavia_form { + position: relative; + display: block; + width: 240px; + height: 25px; +} + +input.oktavia_search { + -webkit-appearance: searchfield; + -webkit-user-select: text; + cursor: auto; + background-color: white; + color: black; + line-height: normal; + display: inline-block; + padding: 1px; + text-align: start; + margin: 2px 0px 2px 0px; + padding: 1px; +} + +div.oktavia_searchresult_box { + display: none; + position: absolute; + width: 500px; + padding: 10px; + right: 10px; + background-color: #ffffff; + -moz-border-radius: 8px; + border-radius: 8px; + -moz-box-shadow: 3px 3px 5px 5px #b5b2b2; + box-shadow: 3px 3px 5px 5px #b5b2b2; + opacity: 0.95; + z-index: 100000; +} + +div.oktavia_close_search_box { + position: absolute; + top: 10px; + right: 10px; + width: 20px; + height: 20px; + text-align: center; + vertical-align: middle; + color: #666; +} + +div.oktavia_close_search_box:hover { + background-color: #d1e8ff; + border: 1px solid gray; +} + +div.oktavia_close_search_box:active { + background-color: #b4c8db; + border: 1px solid gray; +} + + +div.oktavia_searchresult_summary { + color: #444; +} + +div.oktavia_searchresult .entry { + margin: 10px 10px 10px 10px; + color: black; +} + +div.oktavia_searchresult .entry .title { + font-size: normal; +} + +div.oktavia_searchresult .entry .title a:link { + color: #0000EE; + text-decoration: underline; +} + +div.oktavia_searchresult .entry .title a:visited { + color: #551A8B; + text-decoration: underline; +} + +div.oktavia_searchresult .entry .title a:hover { +} + +div.oktavia_searchresult .entry .title a:active { + color: #FF0000; + text-decoration: underline; +} + +div.oktavia_searchresult .entry .url { + font-size: 14px!important; + color: #093; + font-style: normal; + font-family: arial,sans-serif; +} + +div.oktavia_searchresult .entry .resultcontent { + font-size: small; + font-family: arial,sans-serif; + color: #222; +} + +div.oktavia_searchresult .entry .resultcontent .hit { + font-weight: bolder; + text-decoration: underline; +} + +div.oktavia_searchresult .proposal { + margin: 10px 10px 10px 10px; + color: #222; + font-size: small; + font-family: arial,sans-serif; +} + +div.oktavia_searchresult .proposal .option { + background-color: #e8f4ff; + border-bottom: 1px solid blue; +} + +div.oktavia_searchresult .proposal .option:hover { + background-color: #d1e8ff; +} + +div.oktavia_searchresult .proposal .option:active { + background-color: #b4c8db; +} + +div.oktavia_searchresult_nav span.leader { + color: #228; +} + +div.oktavia_searchresult_nav span.page { + background-color: #ffffff; + border: 1px solid #888888; + padding: 5px; + margin: 5px; + color: #222; +} + +div.oktavia_searchresult_nav span.page:hover { + background-color: #d1e8ff; + border: 1px solid #7d98ff; +} + +div.oktavia_searchresult_nav span.page:active { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_nav span.selected { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_nav span.selected:hover { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_nav span.selected:active { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_box span.pr { + position: absolute; + right: 10px; + bottom: 10px; + color: #555; +} + +.highlighted { + background-color: #fbe54e; +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_templates/layout.html b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_templates/layout.html new file mode 100644 index 000000000..bb36ee1f4 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_templates/layout.html @@ -0,0 +1,12 @@ +{% extends "!layout.html" %} + +{% block sidebarlogo %} +{{ super() }} +
+ + + +

Search by Oktavia

+
+
+{% endblock %} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_templates/searchbox.html b/src/web/server/h2o/libh2o/misc/oktavia/templates/sphinx/_templates/searchbox.html new file mode 100644 index 000000000..e69de29bb diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-jquery-ui.js b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-jquery-ui.js new file mode 100644 index 000000000..bd1efb8c5 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-jquery-ui.js @@ -0,0 +1,521 @@ +/** + * @fileOverview + * A UI script that creates search form, loads an index files and show search results. + * It needs jQuery and oktavia-search.js or oktavia-*-search.js + * (stemming library supported versions). + * @author Yoshiki Shibukawa, yoshiki@shibu.jp + */ + +(function ($) +{ + var logosrc; + /** + * @name SearchView + * @class + * Provides searching feature to your website. + * @constructor + * @param {jQeury} node Target node it has a search form and a search result window. + * @param {string} documentRoot Document root folder like '.', '../', '/' + * @param {string} index Index file path. + */ + function SearchView(node, documentRoot, index) + { + var OktaviaSearch = JSX.require("tool/web/oktavia-search.jsx").OktaviaSearch$I; + + /** + * Target node it contains a search form and a search result window. + * @type jQuery + */ + this.node = node; + /** + * Search engine core + * @type OktaviaSearch + */ + this.engine = new OktaviaSearch(5); + if (documentRoot === '') + { + /** + * Document root path + * @type string[] + */ + this.documentRoot = []; + } + else if (documentRoot.slice(-1) === '/') + { + this.documentRoot = documentRoot.slice(0, -1).split(/\//g); + } + else + { + this.documentRoot = documentRoot.split(/\//g); + } + + /** + * It is true if an index file is loaded. + * @type boolean + */ + this.initialized = false; + /** + * It is true if an user search before loading an index. + * @type boolean + */ + this.reserveSearch = false; + + var indexURL; + switch (index.charAt(0)) + { + case '.': + case '/': + indexURL = index; + break; + default: + indexURL = this.getDocumentPath(index); + break; + } + var self = this; + function loadIndex() + { + self.engine.loadIndex$S(window.searchIndex); + self.initialized = true; + window.searchIndex = null; + if (self.reserveSearch) + { + self.search(); + } + self.reserveSearch = false; + } + if (window.searchIndex) + { + loadIndex() + } + else + { + this.loadJavaScript(indexURL, loadIndex); + } + } + + /** + * Changes result page. + * @param {integer} page Page number + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.changePage = function (page) + { + this.engine.setCurrentPage$I(page); + this.updateResult(); + }; + + /** + * Clears a search form and a reult window. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.clearResult = function () + { + $('.oktavia_search', this.node).val(''); + $('.oktavia_searchresult_box', this.node).hide(); + }; + + /** + * Loads an external JavaScript file. + * + * This code is based on: http://os0x.hatenablog.com/entry/20080827/1219815828 + * @param {string} src A JavaScript source file path + * @param {function} callback It is called when the target JavaScript file is loaded + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.loadJavaScript = function (src, callback) + { + var sc = document.createElement('script'); + sc.type = 'text/javascript'; + if (window.ActiveXObject) + { + sc.onreadystatechange = function () + { + if (sc.readyState === 'complete' || sc.readyState === 'loaded') + { + callback(sc.readyState); + } + }; + } + else + { + sc.onload = function () + { + callback('onload'); + }; + } + sc.src = src; + document.body.appendChild(sc); + }; + + /** + * Updates page navigation list. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.updatePageList = function () + { + var self = this; + function createCallback(i) + { + return function () { + self.changePage(i); + }; + } + + var currentPage = String(this.engine.currentPage$()); + var nav = $('.oktavia_searchresult_nav', this.node); + nav.empty(); + var pages = this.engine.pageIndexes$(); + for (var i = 0; i < pages.length; i++) + { + var pageItem = $('').text(pages[i]); + if (pages[i] === '...') + { + pageItem.addClass('leader'); + } + else + { + pageItem.addClass('page'); + if (pages[i] !== currentPage) + { + pageItem.bind('click', createCallback(Number(pages[i]))); + } + else + { + pageItem.addClass('selected'); + } + } + nav.append(pageItem); + } + }; + + /** + * Updates result list in a result window. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.updateResult = function () + { + var totalPages = this.engine.totalPages$(); + var resultslot = $('.oktavia_searchresult', this.node); + resultslot.empty(); + var self = this; + function clearCallback() + { + self.clearResult(); + } + var results = this.engine.getResult$(); + var searchInput = $('.oktavia_search', this.node); + var queryWord = searchInput.val() + for (var i = 0; i < results.length; i++) + { + var result = results[i]; + var url = this.getDocumentPath(result.url.slice(1)) + var entry = $('
', { "class": "entry" }); + var link = $('', { "href": url + this.engine.getHighlight$() }).text(result.title); + link.bind('click', clearCallback); + entry.append($('
', { "class": "title" }).append(link)); + entry.append($('
', { "class": "url" }).text(url)); + entry.append($('
', { "class": "resultcontent" }).html(result.content)); + resultslot.append(entry); + } + this.updatePageList(); + }; + + /** + * Searchs again by using proposal search words. + * @param {string} option Proposal search words + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.searchProposal = function (option) + { + $('.oktavia_search', this.node).val(option); + this.search(); + }; + + /** + * Shows proposals when no result. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.updateProposal = function () + { + var nav = $('.oktavia_searchresult_nav', this.node); + var resultslot = $('.oktavia_searchresult', this.node); + nav.empty(); + resultslot.empty(); + var proposals = this.engine.getProposals$(); + var self = this; + function createCallback(option) + { + return function () + { + self.searchProposal(option); + }; + } + for (var i = 0; i < proposals.length; i++) + { + var proposal = proposals[i]; + var listitem = $('
', {"class": "proposal"}); + listitem.append('Search with: '); + var option = $('', {"class": "option"}); + option.html(proposal.label); + option.bind('click', createCallback(proposal.options)); + listitem.append(option); + listitem.append(' → ' + proposal.count + ' results.'); + resultslot.append(listitem); + } + }; + + /** + * Performs search and shows results. + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.search = function () + { + if (!this.initialized) + { + this.reserveSearch = true; + return; + } + var searchInput = $('.oktavia_search', this.node); + var queryWord = searchInput.val(); + searchInput.blur(); + var self = this; + this.engine.search$SF$IIV$(queryWord, function (total, pages) + { + $('.oktavia_searchresult_box', self.node).fadeIn(); + var summaryNode = $('.oktavia_searchresult_summary', self.node); + if (total === 0) + { + summaryNode.text("No result."); + self.updateProposal(); + } + else + { + summaryNode.text(total + ' results.'); + self.updateResult(); + } + }); + }; + + /** + * Converts file path in index. + * @param {string} filePath Source filepath + * @returns {string} Result filepath + * @memberOf SearchView.prototype + * @method + */ + SearchView.prototype.getDocumentPath = function (filePath) + { + var resultFilePath; + if (filePath.charAt(0) === '/') + { + resultFilePath = filePath; + } + else + { + var elements = filePath.split(/\//g); + var result = this.documentRoot.slice(); + for (var i = 0; i < elements.length; i++) + { + var element = elements[i]; + switch (element) + { + case '.': + break; + case '..': + result = result.slice(0, -1); + break; + default: + result.push(element); + break; + } + } + resultFilePath = result.join('/'); + } + return resultFilePath; + }; + + /** + * Hides all result windows. + * @function + */ + function eraseResultWindow() + { + $('.oktavia_searchresult_box:visible').hide(); + } + + /** + * jQuery plug-in to create search form and window. + * It can receive options from data attributes or an option parameter. + * @param {object} [option] Option + * @param {string} [option.index='search/searchindex.js'] Index file path. + * @param {string} [option.documentRoot='.'] Document root folder. + * @param {string} [option.logo='true'] Show logo in result windows. 'false' or 'disable' or falsy value disable logo. + * @name oktaviaSearch + * @function + */ + jQuery.fn.oktaviaSearch = function (option) + { + var data = { + 'index': 'search/searchindex.js', + 'documentRoot': '.', + 'logo': 'true' + }; + if (window.DOCUMENTATION_OPTIONS) // Sphinx + { + if (window.DOCUMENTATION_OPTIONS.URL_ROOT === '#') + { + data.documentRoot = ''; + } + else + { + data.documentRoot = window.DOCUMENTATION_OPTIONS.URL_ROOT; + } + } + var userData = this.data(); + var key; + for (key in userData) + { + if (userData.hasOwnProperty(key)) + { + data[key] = userData[key]; + } + } + for (key in option) + { + if (option.hasOwnProperty(key)) + { + data[key] = option[key]; + } + } + if (data.logo === 'false' || data.logo === 'disable' || !data.logo) + { + data.logo = false; + } + else + { + data.logo = true; + } + var view = new SearchView(this, data.documentRoot, data.index); + + var form = $('
'); + form.submit(function (event) { + event.stopPropagation(); + setTimeout(function () { + view.search(); + }, 10); + return false; + }); + this.append(form); + var resultForm = $([ + '
', + '', + '
', + '
', + '
', + '
' + ].join('')); + if (data.logo) + { + resultForm.append($('Powered by
Oktavia')); + } + this.append(resultForm); + $('.oktavia_close_search_box', this.node).bind('click', function (event) { + view.clearResult(); + }); + + // Click outside of the result window, close it + resultForm.bind('click', function (event) { + event.stopPropagation(); + }); + }; + + /** + * Global initailization. + * It add some event handlers. + * @name initialize + * @function + */ + function initialize() + { + + function onClick() { + eraseResultWindow(); + return true; + } + function onKeyDown(event) + { + switch (event.keyCode) + { + case 191: // / : focus form + eraseResultWindow(); + var form = $('form.oktavia_form:first input.search'); + if ($(':focus', form).size() === 0) + { + form.focus(); + } + break; + case 74: // j : down + case 75: // k : up + case 72: // h : before page + case 76: // l : next page + case 13: // enter : select + var result = $('.oktavia_searchresult_box:visible:first'); + if (result.size() === 1) + { + switch (event.keyCode) + { + case 74: // j : down + console.log('down'); + break; + case 75: // k : up + console.log('up'); + break; + case 72: // h : before page + console.log('before'); + break; + case 76: // l : next page + console.log('next'); + break; + case 13: // enter : select + console.log('select'); + break; + } + } + break; + } + return true; + } + var version = $.fn.jquery.split(/\./g); + var major = Number(version[0]); + var minor = Number(version[1]); + if (major === 1 && minor < 7) + { + $(document).live('click', onClick); + $(document).live('keydown', onKeyDown); + } + else + { + $(document).on('click', onClick); + $(document).on('keydown', onKeyDown); + } + } + + var logosrc = "data:image/jpeg;base64, /9j/4AAQSkZJRgABAQEASABIAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBwbAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBsAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAAAG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAHMAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFkAAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRHZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJvZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAoAAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUAAAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtvS1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAADHmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAaAAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwAAAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZpRkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAgAFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcAQgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDoAHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8EFwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIARwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkAbMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAgAHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEAbABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBsAHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIARwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBvAGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACADwAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBlAG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBHAGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4ERAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYnBkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwAZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABzAGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdodHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRNAAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8AALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA/ZH///ui///9owAAA9wAAMBs/+EAgEV4aWYAAE1NACoAAAAIAAUBEgADAAAAAQABAAABGgAFAAAAAQAAAEoBGwAFAAAAAQAAAFIBKAADAAAAAQACAACHaQAEAAAAAQAAAFoAAAAAAAAASAAAAAEAAABIAAAAAQACoAIABAAAAAEAAABWoAMABAAAAAEAAAAYAAAAAP/bAEMAAgICAgIBAgICAgICAgMDBgQDAwMDBwUFBAYIBwgICAcICAkKDQsJCQwKCAgLDwsMDQ4ODg4JCxARDw4RDQ4ODv/bAEMBAgICAwMDBgQEBg4JCAkODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODv/AABEIABgAVgMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP3571+dfjn4ofEz4+/tjeJPg38JPE+peAfh/wCE9Q/szxJ4g0namo6xqCoss9tBM6sLe2gRlEkqqXLNtXnFfooa/Lj9jvU7PwH+3T+0D8J/E00Vt4usvHOs3MfmsAbqDUbmO+tp1J6qyN5ZPZkCnqBTQG7pnwW1VP2lde8FfB74qfF3R/E3hrT459f8Val4xutRsIryVd8Nq1ncGRZlYD5zlSoPHTBufD39oH4y+F9H8Xaj8Ur2w8SxeCvES6P4+0tLBIbzSklI8m/t5IwFmt2BBwVB/PI9Z+DOtWfhn/goR+0p8P8AxDcw6f4j1XV7bxLo0dw4U3+nyWwQvHk/MI3RlbH3a+SvG/jnRdbi/bY+JOkyJc+HvF0mm+BvBoiGf+Eh1OGPy3a2H/LUBnHzLkYXNfa0KNGnmlDCxpKVOpGmndXb5knKSb1i7t2cbWtrfW/w+IjiK2WVsRKu41ISm42dlGzajFpWUlZK6le921bS36G+GPHWq61+2r4+8I/2jFdeGdO8N6Xf6fEkKja1wZdz7wNzBgq8E4Fcr8T/ANpDwX4U0bw63hzxb4P1S5vPE8Gm3zveCRLW2WULdzDacHyhwxzhSwJz0Pxp430f4ww/H74qeEvABimudH+FHhxvFdvFcvDqGoW8KSCSztJFB2STBZlL9QFwOWr2L4k+IPh1r/7Fn7MuufDG00638E3XxJ8Pw6dbQQqPsymdlkgcdQ6uCrg8lgc5r1P7EwEcVhqk1zRlyK0bJL92neWm8nqu9m7nl1MzzN4PE0oS5Jpzd5Xb1ntHXaK0v00SXU+s9e+M/wAKfDGg6NqevfEDwrpljq9st1pckt8v+lwtyssYGSyH+8Bj3rQ0v4o/DrW7Hw/daR408OajBrl09ppElveowu50Xc0SY6yBeSvXHavmb/hIr7Uv22vivD8EvhJoXirxTpP2LTPFfibxX4ke0s7aRYQ8VpawiKVgqo4ZtiopJzyea+e7PQfFGteEv2tLyxh8Op8QPAXjrTfE+nW3htX+xLfWtos00UO7DfPGJImJAJYtx2rzcNw5g6tP35ODtF6uL+NpK8VrFJyTu3qleyvp6tfPsxp1PdhGSvJaKS+FNu0no20mrJWTduZ21/Rf4l6/d6N8PWtNC8UeF/C/jLU3Nv4cfXojLb3NyqmTyjGrKzAqrZKnKj5sHGD5j4L8W6fZfC6y+M3xg+IHguN9UQjSxY6iP7G0qFiR5Fs7HNxM2DvmILNgqoVBg+X6B4vsPj9+14vjbQ5xdeB/AXgBbq0kVt0b6tq1uZCPTfDbAA9wZe1eNfCXxXp9x8Ev2UdE8N/DhfiX8Xl8F3uq6PFqet/YdL0qzM5iluZiVcNIWwi7Y2Yc4IzzyUuFaLSlVlLnjbmjdJK8aklrKyWkY3b25no2kj13xhjIUKlCjThySb5Z8rcnZwi0mtWm3L3Vvy7pSlf9C/BnxO+H3xE+2f8ACEeMNB8TPaAG6isboPJCD0LJ94A+uMUV8JaxqPjex/4KWaZ/wmlh4A8NeJrr4bXUslp4Su5pdsI1C1VPtErpGXcndtwgAAPWisswyGlTlB0p6SSfR2+asntvZHDg+Iq/LKNan7ybV1dX+Tu16XZyPjX4k/tYftcfF/xR8Kvgh4b1/wDZ7+EOkanc6R4p8f8AiK2e31O6khkMcsVqqsGUHBwIW3FWy00BG1vWrX/gnT8EtJ+DfhvS/Dmq+NfDPxL0dnmT4m6dfKuvXs8mDI10zKY54jhQIXUqiqoXGM0UV8wfXC6h+xb418baxpK/Fv8Aad+IHjrRtOt5LSOKy8PadpV9PbyAeZDJexRmYI+BuCFc4x0yD6x8Lv2Pfgl8J/GOleINE0rX9e1jSQ40WfxHrM2oJpW8kubWJz5ULMScuqBj60UV00sdiKUXGE2k1Z2fTt6eRhPC0Zu8op9fn39dD2fTfhx4Y0r9oPxP8TrOG9XxXr+mWmnajK92zRNDamQwhYz8qkGR8kcnPPQV5ZqX7Kvwh1L4deMfCbWHiGx0HxF4lTxJLaWOuTwLp+pK277TZ7WH2Zi3JVMKTnjk0UVdLMsXTlzQqNPTq/s/D93TsKeEozVpRT3/AB3+8zT+yX8OIfEY1nSPEvxc8N6xPpdvp+sX2keOL22n1tIF2JJeOrZmn28GbIkOBluK9C+G/wADPhv8Jtc8WXngTR7nR4vEa2/9qWbX0s9vI0KMgkCSFsSOGYyPnMjEsxJ5ooq62bY2rBwnVk09GrvW21/Syt2M45fhozU1BXXWxL8MPgl8Pfg98LtY8H+A9KuNM0TU7+e9vElu3mkaSZQrYdiSFVQqqvRQABXAT/snfCj/AIQX4daNpD+NfC134GspLHw5rmh+JLi01O3tpGLPA86nMsbE5KuGFFFEc1xiqSqKrLmk7t3ers1r30bXo2U8Fh3BQ5FZdLfMS3/ZL+Elp4qtvEUA8aHxWtrPbXmvz+JrqfUNRSWSJ2+0zSMxlwYUCZ4RQVUKCRRRRTnm+Nl8VVv1ZCy3CramvuP/2Q=="; + initialize(); +})(jQuery); + +jQuery(document).ready(function () { + var form = jQuery('#oktavia_search_form'); + if (form.size() > 0) + { + form.oktaviaSearch(); + } +}); diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-search.js b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-search.js new file mode 100644 index 000000000..fce9732a7 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/oktavia-search.js @@ -0,0 +1,6795 @@ +// generatedy by JSX compiler 0.9.24 (2013-04-05 13:45:00 +0900; 1b229cc6a411f674f0f7cf7a79b7a8b3f8eb7414) +var JSX = {}; +(function (JSX) { +/** + * copies the implementations from source interface to target + */ +function $__jsx_merge_interface(target, source) { + for (var k in source.prototype) + if (source.prototype.hasOwnProperty(k)) + target.prototype[k] = source.prototype[k]; +} + +/** + * defers the initialization of the property + */ +function $__jsx_lazy_init(obj, prop, func) { + function reset(obj, prop, value) { + delete obj[prop]; + obj[prop] = value; + return value; + } + + Object.defineProperty(obj, prop, { + get: function () { + return reset(obj, prop, func()); + }, + set: function (v) { + reset(obj, prop, v); + }, + enumerable: true, + configurable: true + }); +} + +/** + * sideeffect().a /= b + */ +function $__jsx_div_assign(obj, prop, divisor) { + return obj[prop] = (obj[prop] / divisor) | 0; +} + +/* + * global functions, renamed to avoid conflict with local variable names + */ +var $__jsx_parseInt = parseInt; +var $__jsx_parseFloat = parseFloat; +var $__jsx_isNaN = isNaN; +var $__jsx_isFinite = isFinite; + +var $__jsx_encodeURIComponent = encodeURIComponent; +var $__jsx_decodeURIComponent = decodeURIComponent; +var $__jsx_encodeURI = encodeURI; +var $__jsx_decodeURI = decodeURI; + +var $__jsx_ObjectToString = Object.prototype.toString; +var $__jsx_ObjectHasOwnProperty = Object.prototype.hasOwnProperty; + +/* + * profiler object, initialized afterwards + */ +function $__jsx_profiler() { +} + +/* + * public interface to JSX code + */ +JSX.require = function (path) { + var m = $__jsx_classMap[path]; + return m !== undefined ? m : null; +}; + +JSX.profilerIsRunning = function () { + return $__jsx_profiler.getResults != null; +}; + +JSX.getProfileResults = function () { + return ($__jsx_profiler.getResults || function () { return {}; })(); +}; + +JSX.postProfileResults = function (url, cb) { + if ($__jsx_profiler.postResults == null) + throw new Error("profiler has not been turned on"); + return $__jsx_profiler.postResults(url, cb); +}; + +JSX.resetProfileResults = function () { + if ($__jsx_profiler.resetResults == null) + throw new Error("profiler has not been turned on"); + return $__jsx_profiler.resetResults(); +}; +JSX.DEBUG = false; +/** + * class _Result extends Object + * @constructor + */ +function _Result() { +} + +/** + * @constructor + * @param {!string} title + * @param {!string} url + * @param {!string} content + * @param {!number} score + */ +function _Result$SSSI(title, url, content, score) { + this.title = title; + this.url = url; + this.content = content; + this.score = score; +}; + +_Result$SSSI.prototype = new _Result; + +/** + * class _Proposal extends Object + * @constructor + */ +function _Proposal() { +} + +/** + * @constructor + * @param {!string} options + * @param {!string} label + * @param {!number} count + */ +function _Proposal$SSI(options, label, count) { + this.options = options; + this.label = label; + this.count = count; +}; + +_Proposal$SSI.prototype = new _Proposal; + +/** + * class OktaviaSearch extends Object + * @constructor + */ +function OktaviaSearch() { +} + +/** + * @constructor + * @param {!number} entriesPerPage + */ +function OktaviaSearch$I(entriesPerPage) { + this._queries = null; + this._highlight = ""; + this._result = null; + this._proposals = null; + this._currentFolderDepth = 0; + this._oktavia = new Oktavia$(); + this._entriesPerPage = entriesPerPage; + this._currentPage = 1; + this._queryString = null; + this._callback = null; + OktaviaSearch._instance = this; +}; + +OktaviaSearch$I.prototype = new OktaviaSearch; + +/** + * @param {Stemmer} stemmer + */ +OktaviaSearch.setStemmer$LStemmer$ = function (stemmer) { + /** @type {Oktavia} */ + var this$0; + if (OktaviaSearch._instance) { + this$0 = OktaviaSearch._instance._oktavia; + this$0._stemmer = stemmer; + } else { + OktaviaSearch._stemmer = stemmer; + } +}; + +var OktaviaSearch$setStemmer$LStemmer$ = OktaviaSearch.setStemmer$LStemmer$; + +/** + * @param {!string} index + */ +OktaviaSearch.prototype.loadIndex$S = function (index) { + /** @type {Oktavia} */ + var this$0; + /** @type {Stemmer} */ + var stemmer$0; + if (OktaviaSearch._stemmer) { + this$0 = this._oktavia; + stemmer$0 = OktaviaSearch._stemmer; + this$0._stemmer = stemmer$0; + } + this._oktavia.load$S(Binary$base64decode$S(index)); + if (this._queryString) { + this.search$SF$IIV$(this._queryString, this._callback); + this._queryString = null; + this._callback = null; + } +}; + +/** + * @param {!string} queryString + * @param {*} callback + */ +OktaviaSearch.prototype.search$SF$IIV$ = function (queryString, callback) { + /** @type {QueryStringParser} */ + var queryParser; + /** @type {SearchSummary} */ + var summary; + /** @type {Array.} */ + var _result$0; + if (this._oktavia) { + queryParser = ({queries: [ ]}); + this._queries = QueryStringParser$parse$LQueryStringParser$S(queryParser, queryString); + this._highlight = QueryStringParser$highlight$LQueryStringParser$(queryParser); + summary = this._oktavia.search$ALQuery$(this._queries); + if (SearchSummary$size$LSearchSummary$(summary) > 0) { + this._result = this._sortResult$LSearchSummary$(summary); + this._proposals = [ ]; + this._currentPage = 1; + } else { + this._result = [ ]; + if (this._queries.length > 1) { + this._proposals = SearchSummary$getProposal$LSearchSummary$(summary); + } else { + this._proposals = [ ]; + } + this._currentPage = 1; + } + callback((_result$0 = this._result).length, Math.ceil(_result$0.length / this._entriesPerPage)); + } else { + this._queryString = queryString; + this._callback = callback; + } +}; + +/** + * @return {!number} + */ +OktaviaSearch.prototype.resultSize$ = function () { + return (this._result.length | 0); +}; + +/** + * @return {!number} + */ +OktaviaSearch.prototype.totalPages$ = function () { + return (Math.ceil(this._result.length / this._entriesPerPage) | 0); +}; + +/** + * @return {!number} + */ +OktaviaSearch.prototype.currentPage$ = function () { + return this._currentPage; +}; + +/** + * @param {!number} page + */ +OktaviaSearch.prototype.setCurrentPage$I = function (page) { + this._currentPage = page; +}; + +/** + * @return {!boolean} + */ +OktaviaSearch.prototype.hasPrevPage$ = function () { + return this._currentPage !== 1; +}; + +/** + * @return {!boolean} + */ +OktaviaSearch.prototype.hasNextPage$ = function () { + return this._currentPage !== Math.ceil(this._result.length / this._entriesPerPage); +}; + +/** + * @return {Array.} + */ +OktaviaSearch.prototype.pageIndexes$ = function () { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var total; + /** @type {!number} */ + var i; + result = [ ]; + total = Math.ceil(this._result.length / this._entriesPerPage); + if (total < 10) { + for (i = 1; i <= total; i++) { + result.push(i + ""); + } + } else { + if (this._currentPage <= 5) { + for (i = 1; i <= 7; i++) { + result.push(i + ""); + } + result.push('...', total + ""); + } else { + if (total - 5 <= this._currentPage) { + result.push('1', '...'); + for (i = total - 8; i <= total; i++) { + result.push(i + ""); + } + } else { + result.push('1', '...'); + for (i = this._currentPage - 3; i <= this._currentPage + 3; i++) { + result.push(i + ""); + } + result.push('...', total + ""); + } + } + } + return result; +}; + +/** + * @return {Array.} + */ +OktaviaSearch.prototype.getResult$ = function () { + /** @type {Style} */ + var style; + /** @type {!number} */ + var start; + /** @type {!number} */ + var last; + /** @type {Metadata} */ + var metadata; + /** @type {!number} */ + var num; + /** @type {Array.} */ + var results; + /** @type {!number} */ + var i; + /** @type {SearchUnit} */ + var unit; + /** @type {Array.} */ + var info; + /** @type {!string} */ + var content; + /** @type {Array.} */ + var positions; + /** @type {!number} */ + var end; + /** @type {!boolean} */ + var split; + /** @type {!number} */ + var j; + /** @type {Position} */ + var pos; + /** @type {!string} */ + var text; + /** @type {Oktavia} */ + var this$0; + /** @type {!number} */ + var position$0; + /** @type {!number} */ + var _currentPage$0; + /** @type {!number} */ + var _entriesPerPage$0; + style = new Style$S('html'); + start = ((_currentPage$0 = this._currentPage) - 1) * (_entriesPerPage$0 = this._entriesPerPage); + last = Math.min(_currentPage$0 * _entriesPerPage$0, this._result.length); + this$0 = this._oktavia; + metadata = this$0._metadatas[this$0._metadataLabels[0]]; + num = 250; + results = [ ]; + for (i = start; i < last; i++) { + unit = this._result[i]; + info = metadata.getInformation$I(unit.id).split(Oktavia.eob); + content = metadata.getContent$I(unit.id); + start = 0; + positions = SearchUnit$getPositions$LSearchUnit$(unit); + if (content.indexOf(info[0]) === 1) { + content = content.slice(info[0].length + 2, content.length); + start += info[0].length + 2; + } + end = start + num; + split = false; + if (positions[0].position > end - positions[0].word.length) { + end = positions[0].position + Math.floor(num / 2); + split = true; + } + for (j = positions.length - 1; j > -1; j--) { + pos = positions[j]; + if (pos.position + pos.word.length < end) { + content = [ content.slice(0, pos.position - start), style.convert$S('*').replace('*', content.slice((position$0 = pos.position) - start, position$0 + pos.word.length - start)), content.slice(pos.position + pos.word.length - start, content.length) ].join(''); + } + } + if (split) { + text = [ content.slice(0, Math.floor(num / 2)) + ' ...', content.slice(- Math.floor(num / 2), end - start) ].join('
'); + } else { + text = content.slice(0, end - start) + ' ...
'; + } + text = text.replace(Oktavia.eob, ' ').replace(/()()+/, '

'); + results.push(({title: info[0], url: info[1], content: text, score: unit.score})); + } + return results; +}; + +/** + * @return {!string} + */ +OktaviaSearch.prototype.getHighlight$ = function () { + return this._highlight; +}; + +/** + * @return {Array.} + */ +OktaviaSearch.prototype.getProposals$ = function () { + /** @type {Style} */ + var style; + /** @type {Array.} */ + var results; + /** @type {!number} */ + var i; + /** @type {Proposal} */ + var proposal; + /** @type {Array.} */ + var label; + /** @type {Array.} */ + var option; + /** @type {!number} */ + var j; + style = new Style$S('html'); + results = [ ]; + if (this._queries.length > 1) { + for (i = 0; i < this._proposals.length; i++) { + proposal = this._proposals[i]; + if (proposal.expect > 0) { + label = [ ]; + option = [ ]; + for (j = 0; j < this._queries.length; j++) { + if (j !== proposal.omit) { + label.push(style.convert$S('' + this._queries[j].toString() + '')); + option.push(this._queries[j].toString()); + } else { + label.push(style.convert$S('' + this._queries[j].toString() + '')); + } + } + results.push(({options: option.join(' '), label: label.join(' '), count: proposal.expect})); + } + } + } + return results; +}; + +/** + * @param {SearchSummary} summary + * @return {Array.} + */ +OktaviaSearch.prototype._sortResult$LSearchSummary$ = function (summary) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var score; + /** @type {SearchUnit} */ + var unit; + /** @type {!string} */ + var pos; + /** @type {Position} */ + var position; + for (i = 0; i < summary.result.units.length; i++) { + score = 0; + unit = summary.result.units[i]; + for (pos in unit.positions) { + position = unit.positions[pos]; + if (this._oktavia.wordPositionType$I(position.position)) { + score += 10; + } else { + score += 1; + } + if (! position.stemmed) { + score += 2; + } + } + unit.score = (score | 0); + } + return SearchSummary$getSortedResult$LSearchSummary$(summary); +}; + +/** + * class _Main extends Object + * @constructor + */ +function _Main() { +} + +/** + * @constructor + */ +function _Main$() { +}; + +_Main$.prototype = new _Main; + +/** + * @param {Array.} args + */ +_Main.main$AS = function (args) { +}; + +var _Main$main$AS = _Main.main$AS; + +/** + * class Oktavia extends Object + * @constructor + */ +function Oktavia() { +} + +/** + * @constructor + */ +function Oktavia$() { + /** @type {Array.} */ + var _utf162compressCode$0; + this._compressCode2utf16 = null; + this._fmindex = new FMIndex$(); + this._metadatas = ({ }); + this._metadataLabels = [ ]; + this._stemmer = null; + this._stemmingResult = ({ }); + _utf162compressCode$0 = this._utf162compressCode = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; + _utf162compressCode$0.length = 65536; + this._compressCode2utf16 = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; +}; + +Oktavia$.prototype = new Oktavia; + +/** + * @param {Stemmer} stemmer + */ +Oktavia.prototype.setStemmer$LStemmer$ = function (stemmer) { + this._stemmer = stemmer; +}; + +/** + * @return {Metadata} + */ +Oktavia.prototype.getPrimaryMetadata$ = function () { + return this._metadatas[this._metadataLabels[0]]; +}; + +/** + * @param {!string} key + * @return {Section} + */ +Oktavia.prototype.addSection$S = function (key) { + /** @type {Section} */ + var section; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + section = new Section$LOktavia$(this); + this._metadatas[key] = section; + return section; +}; + +/** + * @param {!string} key + * @return {Section} + */ +Oktavia.prototype.getSection$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + * @param {!string} key + * @return {Splitter} + */ +Oktavia.prototype.addSplitter$S = function (key) { + /** @type {Splitter} */ + var splitter; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + splitter = new Splitter$LOktavia$(this); + this._metadatas[key] = splitter; + return splitter; +}; + +/** + * @param {!string} key + * @return {Splitter} + */ +Oktavia.prototype.getSplitter$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + * @param {!string} key + * @param {Array.} headers + * @return {Table} + */ +Oktavia.prototype.addTable$SAS = function (key, headers) { + /** @type {Table} */ + var table; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + table = new Table$LOktavia$AS(this, headers); + this._metadatas[key] = table; + return table; +}; + +/** + * @param {!string} key + * @return {Table} + */ +Oktavia.prototype.getTable$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + * @param {!string} key + * @return {Block} + */ +Oktavia.prototype.addBlock$S = function (key) { + /** @type {Block} */ + var block; + if (this._metadataLabels.indexOf(key) !== -1) { + throw new Error('Metadata name ' + key + ' is already exists'); + } + this._metadataLabels.push(key); + block = new Block$LOktavia$(this); + this._metadatas[key] = block; + return block; +}; + +/** + * @param {!string} key + * @return {Block} + */ +Oktavia.prototype.getBlock$S = function (key) { + if (this._metadataLabels.indexOf(key) === -1) { + throw new Error('Metadata name ' + key + " does't exists"); + } + return this._metadatas[key]; +}; + +/** + */ +Oktavia.prototype.addEndOfBlock$ = function () { + this._fmindex.push$S(Oktavia.eob); +}; + +/** + * @param {!string} words + */ +Oktavia.prototype.addWord$S = function (words) { + /** @type {Array.} */ + var str; + /** @type {!number} */ + var i; + /** @type {!number} */ + var charCode; + /** @type {undefined|!string} */ + var newCharCode; + str = [ ]; + str.length = words.length; + for (i = 0; i < words.length; i++) { + charCode = words.charCodeAt(i); + newCharCode = this._utf162compressCode[charCode]; + if (newCharCode == null) { + newCharCode = String.fromCharCode(this._compressCode2utf16.length); + this._utf162compressCode[charCode] = newCharCode; + this._compressCode2utf16.push(String.fromCharCode(charCode)); + } + str.push(newCharCode); + } + this._fmindex.push$S(str.join('')); +}; + +/** + * @param {!string} words + * @param {!boolean} stemming + */ +Oktavia.prototype.addWord$SB = function (words, stemming) { + /** @type {Array.} */ + var wordList; + /** @type {!number} */ + var i; + /** @type {undefined|!string} */ + var originalWord; + /** @type {!string} */ + var smallWord; + /** @type {undefined|!string} */ + var registerWord; + /** @type {!string} */ + var baseWord; + /** @type {!string} */ + var compressedCodeWord; + /** @type {Array.} */ + var stemmedList; + this.addWord$S(words); + wordList = words.split(/\s+/); + for (i = 0; i < wordList.length; i++) { + originalWord = wordList[i]; + smallWord = originalWord.slice(0, 1).toLowerCase() + originalWord.slice(1); + registerWord = null; + if (stemming && this._stemmer) { + baseWord = this._stemmer.stemWord$S(originalWord.toLowerCase()); + if (originalWord.indexOf(baseWord) === -1) { + registerWord = baseWord; + } + } else { + if (originalWord != smallWord) { + registerWord = smallWord; + } + } + if (registerWord) { + compressedCodeWord = this._convertToCompressionCode$S(originalWord); + stemmedList = this._stemmingResult[registerWord]; + if (! stemmedList) { + stemmedList = [ compressedCodeWord ]; + this._stemmingResult[registerWord] = stemmedList; + } else { + if (stemmedList.indexOf(compressedCodeWord) === -1) { + stemmedList.push(compressedCodeWord); + } + } + } + } +}; + +/** + * @param {!string} keyword + * @return {!string} + */ +Oktavia.prototype._convertToCompressionCode$S = function (keyword) { + /** @type {Array.} */ + var resultChars; + /** @type {!number} */ + var i; + /** @type {undefined|!string} */ + var chr; + resultChars = [ ]; + for (i = 0; i < keyword.length; i++) { + chr = this._utf162compressCode[keyword.charCodeAt(i)]; + if (chr == null) { + resultChars.push(Oktavia.unknown); + } else { + resultChars.push(chr); + } + } + return resultChars.join(''); +}; + +/** + * @param {!string} keyword + * @param {!boolean} stemming + * @return {Array.} + */ +Oktavia.prototype.rawSearch$SB = function (keyword, stemming) { + /** @type {Array.} */ + var result; + /** @type {!string} */ + var baseWord; + /** @type {Array.} */ + var stemmedList; + /** @type {!number} */ + var i; + /** @type {undefined|!string} */ + var word; + if (stemming) { + result = [ ]; + if (this._stemmer) { + baseWord = this._stemmer.stemWord$S(keyword.toLowerCase()); + stemmedList = this._stemmingResult[baseWord]; + if (stemmedList) { + for (i = 0; i < stemmedList.length; i++) { + word = stemmedList[i]; + result = result.concat(this._fmindex.search$S(word)); + } + } + } + } else { + result = this._fmindex.search$S(this._convertToCompressionCode$S(keyword)); + } + return result; +}; + +/** + * @param {Array.} queries + * @return {SearchSummary} + */ +Oktavia.prototype.search$ALQuery$ = function (queries) { + /** @type {SearchSummary} */ + var summary; + /** @type {!number} */ + var i; + /** @type {SingleResult} */ + var result$0; + summary = ({sourceResults: [ ], result: null, oktavia: this}); + for (i = 0; i < queries.length; i++) { + result$0 = this._searchQuery$LQuery$(queries[i]); + summary.sourceResults.push(result$0); + } + summary.result = SearchSummary$mergeResult$LSearchSummary$ALSingleResult$(summary, summary.sourceResults); + return summary; +}; + +/** + * @param {Query} query + * @return {SingleResult} + */ +Oktavia.prototype._searchQuery$LQuery$ = function (query) { + /** @type {SingleResult} */ + var result; + /** @type {Array.} */ + var positions; + result = new SingleResult$SBB(query.word, query.or, query.not); + if (query.raw) { + positions = this.rawSearch$SB(query.word, false); + } else { + positions = this.rawSearch$SB(query.word, false).concat(this.rawSearch$SB(query.word, true)); + } + this._metadatas[this._metadataLabels[0]].grouping$LSingleResult$AISB(result, positions, query.word, ! query.raw); + return result; +}; + +/** + */ +Oktavia.prototype.build$ = function () { + this.build$IB(5, false); +}; + +/** + * @param {!number} cacheDensity + * @param {!boolean} verbose + */ +Oktavia.prototype.build$IB = function (cacheDensity, verbose) { + /** @type {!string} */ + var key; + /** @type {!number} */ + var cacheRange; + /** @type {!number} */ + var maxChar; + for (key in this._metadatas) { + this._metadatas[key]._build$(); + } + cacheRange = Math.round(Math.max(1, 100 / Math.min(100, Math.max(0.01, cacheDensity)))); + maxChar = this._compressCode2utf16.length; + this._fmindex.build$SIIB(Oktavia.eof, maxChar, cacheRange, verbose); +}; + +/** + * @return {!string} + */ +Oktavia.prototype.dump$ = function () { + return this.dump$B(false); +}; + +/** + * @param {!boolean} verbose + * @return {!string} + */ +Oktavia.prototype.dump$B = function (verbose) { + /** @type {!string} */ + var header; + /** @type {!string} */ + var fmdata; + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + /** @type {CompressionReport} */ + var report; + /** @type {undefined|!string} */ + var name; + /** @type {!string} */ + var data; + header = Binary$dumpString$SLCompressionReport$("oktavia-01", null).slice(1); + if (verbose) { + console.log("Source text size: " + (this._fmindex.size$() * 2 + "") + ' bytes'); + } + fmdata = this._fmindex.dump$B(verbose); + result = [ header, fmdata ]; + result.push(Binary$dump16bitNumber$I(this._compressCode2utf16.length)); + for (i = 3; i < this._compressCode2utf16.length; i++) { + result.push(this._compressCode2utf16[i]); + } + if (verbose) { + console.log('Char Code Map: ' + (this._compressCode2utf16.length * 2 - 2 + "") + ' bytes'); + } + report = ({source: 0, result: 0}); + result.push(Binary$dumpStringListMap$HASLCompressionReport$(this._stemmingResult, report)); + if (verbose) { + console.log('Stemmed Word Table: ' + (result[result.length - 1].length + "") + ' bytes (' + (Math.round(report.result * 100.0 / report.source) + "") + '%)'); + } + result.push(Binary$dump16bitNumber$I(this._metadataLabels.length)); + for (i = 0; i < this._metadataLabels.length; i++) { + report = ({source: 0, result: 0}); + name = this._metadataLabels[i]; + data = this._metadatas[name]._dump$LCompressionReport$(report); + result.push(Binary$dumpString$SLCompressionReport$(name, report), data); + if (verbose) { + console.log('Meta Data ' + name + ': ' + (data.length * 2 + "") + ' bytes (' + (Math.round(report.result * 100.0 / report.source) + "") + '%)'); + } + } + return result.join(''); +}; + +/** + * @param {!string} data + */ +Oktavia.prototype.load$S = function (data) { + /** @type {!string} */ + var header; + /** @type {!number} */ + var offset; + /** @type {!number} */ + var charCodeCount; + /** @type {!number} */ + var i; + /** @type {!number} */ + var charCode; + /** @type {LoadedStringListMapResult} */ + var stemmedWords; + /** @type {!number} */ + var metadataCount; + /** @type {LoadedStringResult} */ + var nameResult; + /** @type {!string} */ + var name; + /** @type {!number} */ + var type; + header = Binary$dumpString$SLCompressionReport$("oktavia-01", null).slice(1); + if (data.slice(0, 5) !== header) { + throw new Error('Invalid data file'); + } + this._metadatas = ({ }); + this._metadataLabels = [ ]; + offset = 5; + offset = this._fmindex.load$SI(data, offset); + charCodeCount = Binary$load16bitNumber$SI(data, offset++); + this._compressCode2utf16 = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; + this._utf162compressCode = [ Oktavia.eof, Oktavia.eob, Oktavia.unknown ]; + for (i = 3; i < charCodeCount; i++) { + charCode = Binary$load16bitNumber$SI(data, offset++); + this._compressCode2utf16.push(String.fromCharCode(charCode)); + this._utf162compressCode[charCode] = String.fromCharCode(i); + } + stemmedWords = Binary$loadStringListMap$SI(data, offset); + this._stemmingResult = stemmedWords.result; + offset = stemmedWords.offset; + metadataCount = Binary$load16bitNumber$SI(data, offset++); + for (i = 0; i < metadataCount; i++) { + nameResult = Binary$loadString$SI(data, offset); + name = nameResult.result; + offset = nameResult.offset; + type = Binary$load16bitNumber$SI(data, offset++); + switch (type) { + case 0: + offset = Section$_load$LOktavia$SSI(this, name, data, offset); + break; + case 1: + offset = Splitter$_load$LOktavia$SSI(this, name, data, offset); + break; + case 2: + offset = Table$_load$LOktavia$SSI(this, name, data, offset); + break; + case 3: + offset = Block$_load$LOktavia$SSI(this, name, data, offset); + break; + } + } +}; + +/** + * @return {!number} + */ +Oktavia.prototype.contentSize$ = function () { + /** @type {FMIndex} */ + var this$0; + this$0 = this._fmindex; + return this$0._substr.length; +}; + +/** + * @param {!number} position + * @return {!number} + */ +Oktavia.prototype.wordPositionType$I = function (position) { + /** @type {!number} */ + var result; + /** @type {!string} */ + var ahead; + result = 0; + if (position === 0) { + result = 4; + } else { + ahead = this._fmindex.getSubstring$II(position - 1, 1); + if (/\s/.test(ahead)) { + result = 2; + } else { + if (/\W/.test(ahead)) { + result = 1; + } else { + if (Oktavia.eob === ahead) { + result = 3; + } + } + } + } + return (result | 0); +}; + +/** + * @param {!number} position + * @param {!number} length + * @return {!string} + */ +Oktavia.prototype._getSubstring$II = function (position, length) { + /** @type {!string} */ + var result; + /** @type {Array.} */ + var str; + /** @type {!number} */ + var i; + result = this._fmindex.getSubstring$II(position, length); + str = [ ]; + for (i = 0; i < result.length; i++) { + str.push(this._compressCode2utf16[result.charCodeAt(i)]); + } + return str.join(''); +}; + +/** + * class Binary extends Object + * @constructor + */ +function Binary() { +} + +/** + * @constructor + */ +function Binary$() { +}; + +Binary$.prototype = new Binary; + +/** + * @param {!number} num + * @return {!string} + */ +Binary.dump32bitNumber$N = function (num) { + /** @type {Array.} */ + var result; + result = [ String.fromCharCode(Math.floor(num / 65536)) ]; + result.push(String.fromCharCode(num % 65536)); + return result.join(""); +}; + +var Binary$dump32bitNumber$N = Binary.dump32bitNumber$N; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {!number} + */ +Binary.load32bitNumber$SI = function (buffer, offset) { + /** @type {!number} */ + var result; + result = buffer.charCodeAt(offset) * 65536 + buffer.charCodeAt(offset + 1); + return result; +}; + +var Binary$load32bitNumber$SI = Binary.load32bitNumber$SI; + +/** + * @param {!number} num + * @return {!string} + */ +Binary.dump16bitNumber$I = function (num) { + return String.fromCharCode(num % 65536); +}; + +var Binary$dump16bitNumber$I = Binary.dump16bitNumber$I; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {!number} + */ +Binary.load16bitNumber$SI = function (buffer, offset) { + return (buffer.charCodeAt(offset) | 0); +}; + +var Binary$load16bitNumber$SI = Binary.load16bitNumber$SI; + +/** + * @param {!string} str + * @return {!string} + */ +Binary.dumpString$S = function (str) { + return Binary$dumpString$SLCompressionReport$(str, null); +}; + +var Binary$dumpString$S = Binary.dumpString$S; + +/** + * @param {!string} str + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dumpString$SLCompressionReport$ = function (str, report) { + /** @type {!number} */ + var length; + /** @type {!boolean} */ + var compress; + /** @type {Array.} */ + var charCodes; + /** @type {!number} */ + var i; + /** @type {!number} */ + var charCode; + /** @type {Array.} */ + var result; + /** @type {undefined|!number} */ + var bytes; + if (str.length > 32768) { + str = str.slice(0, 32768); + } + length = str.length; + compress = true; + charCodes = [ ]; + for (i = 0; i < length; i++) { + charCode = str.charCodeAt(i); + if (charCode > 255) { + compress = false; + break; + } + charCodes.push(charCode); + } + if (compress) { + result = [ Binary$dump16bitNumber$I(length + 32768) ]; + for (i = 0; i < length; i += 2) { + bytes = charCodes[i]; + if (i !== length - 1) { + bytes += charCodes[i + 1] << 8; + } + result.push(String.fromCharCode(bytes % 65536)); + } + if (report) { + CompressionReport$add$LCompressionReport$II(report, length, Math.ceil(length / 2)); + } + } else { + result = [ Binary$dump16bitNumber$I(length), str ]; + if (report) { + CompressionReport$add$LCompressionReport$II(report, length, length); + } + } + return result.join(''); +}; + +var Binary$dumpString$SLCompressionReport$ = Binary.dumpString$SLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedStringResult} + */ +Binary.loadString$SI = function (buffer, offset) { + return new LoadedStringResult$SI(buffer, offset); +}; + +var Binary$loadString$SI = Binary.loadString$SI; + +/** + * @param {Array.} strList + * @return {!string} + */ +Binary.dumpStringList$AS = function (strList) { + return Binary$dumpStringList$ASLCompressionReport$(strList, null); +}; + +var Binary$dumpStringList$AS = Binary.dumpStringList$AS; + +/** + * @param {Array.} strList + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dumpStringList$ASLCompressionReport$ = function (strList, report) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + result = [ Binary$dump32bitNumber$N(strList.length) ]; + for (i = 0; i < strList.length; i++) { + result.push(Binary$dumpString$SLCompressionReport$(strList[i], report)); + } + return result.join(''); +}; + +var Binary$dumpStringList$ASLCompressionReport$ = Binary.dumpStringList$ASLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedStringListResult} + */ +Binary.loadStringList$SI = function (buffer, offset) { + return new LoadedStringListResult$SI(buffer, offset); +}; + +var Binary$loadStringList$SI = Binary.loadStringList$SI; + +/** + * @param {Object.>} strMap + * @return {!string} + */ +Binary.dumpStringListMap$HAS = function (strMap) { + return Binary$dumpStringListMap$HASLCompressionReport$(strMap, null); +}; + +var Binary$dumpStringListMap$HAS = Binary.dumpStringListMap$HAS; + +/** + * @param {Object.>} strMap + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dumpStringListMap$HASLCompressionReport$ = function (strMap, report) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var counter; + /** @type {!string} */ + var key; + result = [ ]; + counter = 0; + for (key in strMap) { + result.push(Binary$dumpString$SLCompressionReport$(key, report)); + result.push(Binary$dumpStringList$ASLCompressionReport$(strMap[key], report)); + counter++; + } + return Binary$dump32bitNumber$N(counter) + result.join(''); +}; + +var Binary$dumpStringListMap$HASLCompressionReport$ = Binary.dumpStringListMap$HASLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedStringListMapResult} + */ +Binary.loadStringListMap$SI = function (buffer, offset) { + return new LoadedStringListMapResult$SI(buffer, offset); +}; + +var Binary$loadStringListMap$SI = Binary.loadStringListMap$SI; + +/** + * @param {Array.} array + * @return {!string} + */ +Binary.dump32bitNumberList$AN = function (array) { + return Binary$dump32bitNumberList$ANLCompressionReport$(array, null); +}; + +var Binary$dump32bitNumberList$AN = Binary.dump32bitNumberList$AN; + +/** + * @param {Array.} array + * @param {CompressionReport} report + * @return {!string} + */ +Binary.dump32bitNumberList$ANLCompressionReport$ = function (array, report) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var index; + /** @type {!number} */ + var inputLength; + /** @type {!number} */ + var length; + /** @type {!string} */ + var resultString; + /** @type {!number} */ + var value1$0; + /** @type {!number} */ + var value2$0; + result = [ Binary$dump32bitNumber$N(array.length) ]; + index = 0; + inputLength = array.length; + while (index < inputLength) { + if (array[index] == 0) { + length = Binary$_countZero$ANI(array, index); + result.push(Binary$_zeroBlock$I(length)); + index += length; + } else { + if (Binary$_shouldZebraCode$ANI(array, index)) { + result.push(Binary$_createZebraCode$ANI(array, index)); + value1$0 = array.length; + value2$0 = index + 15; + index = (value1$0 <= value2$0 ? value1$0 : value2$0); + } else { + length = Binary$_searchDoubleZero$ANI(array, index); + result.push(Binary$_nonZeroBlock$ANII(array, index, length)); + if (length === 0) { + throw new Error(''); + } + index += length; + } + } + } + resultString = result.join(''); + if (report) { + CompressionReport$add$LCompressionReport$II(report, array.length * 2 + 2, resultString.length); + } + return resultString; +}; + +var Binary$dump32bitNumberList$ANLCompressionReport$ = Binary.dump32bitNumberList$ANLCompressionReport$; + +/** + * @param {!string} buffer + * @param {!number} offset + * @return {LoadedNumberListResult} + */ +Binary.load32bitNumberList$SI = function (buffer, offset) { + return new LoadedNumberListResult$SI(buffer, offset); +}; + +var Binary$load32bitNumberList$SI = Binary.load32bitNumberList$SI; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!number} + */ +Binary._countZero$ANI = function (array, offset) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var array$len$0; + for ((i = offset, array$len$0 = array.length); i < array$len$0; i++) { + if (array[i] != 0) { + return (i - offset | 0); + } + } + return (array.length - offset | 0); +}; + +var Binary$_countZero$ANI = Binary._countZero$ANI; + +/** + * @param {!number} length + * @return {!string} + */ +Binary._zeroBlock$I = function (length) { + /** @type {Array.} */ + var result; + result = [ ]; + while (length > 0) { + if (length > 16384) { + result.push(Binary$dump16bitNumber$I(16383)); + length -= 16384; + } else { + result.push(Binary$dump16bitNumber$I(length - 1)); + length = 0; + } + } + return result.join(''); +}; + +var Binary$_zeroBlock$I = Binary._zeroBlock$I; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!boolean} + */ +Binary._shouldZebraCode$ANI = function (array, offset) { + /** @type {!number} */ + var change; + /** @type {!boolean} */ + var isLastZero; + /** @type {!number} */ + var i; + if (array.length - offset < 16) { + return true; + } + change = 0; + isLastZero = false; + for (i = offset; i < offset + 15; i++) { + if (array[i] == 0) { + if (! isLastZero) { + isLastZero = true; + change++; + } + } else { + if (isLastZero) { + isLastZero = false; + change++; + } + } + } + return change > 2; +}; + +var Binary$_shouldZebraCode$ANI = Binary._shouldZebraCode$ANI; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!number} + */ +Binary._searchDoubleZero$ANI = function (array, offset) { + /** @type {!boolean} */ + var isLastZero; + /** @type {!number} */ + var i; + /** @type {!number} */ + var array$len$0; + isLastZero = false; + for ((i = offset, array$len$0 = array.length); i < array$len$0; i++) { + if (array[i] == 0) { + if (isLastZero) { + return (i - offset - 1 | 0); + } + isLastZero = true; + } else { + isLastZero = false; + } + } + return (array.length - offset | 0); +}; + +var Binary$_searchDoubleZero$ANI = Binary._searchDoubleZero$ANI; + +/** + * @param {Array.} array + * @param {!number} offset + * @param {!number} length + * @return {!string} + */ +Binary._nonZeroBlock$ANII = function (array, offset, length) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var blockLength; + /** @type {!number} */ + var i; + result = [ ]; + while (length > 0) { + if (length > 16384) { + blockLength = 16384; + length -= 16384; + } else { + blockLength = length; + length = 0; + } + result.push(Binary$dump16bitNumber$I(blockLength - 1 + 0x4000)); + for (i = offset; i < offset + blockLength; i++) { + result.push(Binary$dump32bitNumber$N(array[i])); + } + offset += blockLength; + } + return result.join(''); +}; + +var Binary$_nonZeroBlock$ANII = Binary._nonZeroBlock$ANII; + +/** + * @param {Array.} array + * @param {!number} offset + * @return {!string} + */ +Binary._createZebraCode$ANI = function (array, offset) { + /** @type {!number} */ + var last; + /** @type {!number} */ + var code; + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + /** @type {!number} */ + var value1$0; + /** @type {!number} */ + var value2$0; + value1$0 = offset + 15; + value2$0 = array.length; + last = (value1$0 <= value2$0 ? value1$0 : value2$0); + code = 0x8000; + result = [ ]; + for (i = offset; i < last; i++) { + if (array[i] != 0) { + result.push(Binary$dump32bitNumber$N(array[i])); + code = code + (0x1 << i - offset); + } + } + return String.fromCharCode(code) + result.join(''); +}; + +var Binary$_createZebraCode$ANI = Binary._createZebraCode$ANI; + +/** + * @param {!string} str + * @return {!string} + */ +Binary.base64encode$S = function (str) { + /** @type {Array.} */ + var out; + /** @type {Array.} */ + var source; + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {!number} */ + var len; + /** @type {!number} */ + var c1; + /** @type {undefined|!number} */ + var c2; + /** @type {undefined|!number} */ + var c3; + out = [ ]; + source = [ ]; + for (i = 0; i < str.length; i++) { + code = str.charCodeAt(i); + source.push(code & 0x00ff, code >>> 8); + } + len = str.length * 2; + i = 0; + while (i < len) { + c1 = source[i++] & 0xff; + if (i === len) { + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c1 >> 2)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c1 & 0x3) << 4)); + out.push("=="); + break; + } + c2 = source[i++]; + if (i === len) { + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c1 >> 2)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c1 & 0x3) << 4 | (c2 & 0xF0) >> 4)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c2 & 0xF) << 2)); + out.push("="); + break; + } + c3 = source[i++]; + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c1 >> 2)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c1 & 0x3) << 4 | (c2 & 0xF0) >> 4)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((c2 & 0xF) << 2 | (c3 & 0xC0) >> 6)); + out.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c3 & 0x3F)); + } + return out.join(''); +}; + +var Binary$base64encode$S = Binary.base64encode$S; + +/** + * @param {Array.} source + * @return {!string} + */ +Binary._mergeCharCode$AI = function (source) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + result = [ ]; + for (i = 0; i < source.length; i += 2) { + result.push(String.fromCharCode(source[i] + (source[i + 1] << 8))); + } + return result.join(''); +}; + +var Binary$_mergeCharCode$AI = Binary._mergeCharCode$AI; + +/** + * @param {!string} str + * @return {!string} + */ +Binary.base64decode$S = function (str) { + /** @type {!number} */ + var len; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var out; + /** @type {undefined|!number} */ + var c1; + /** @type {undefined|!number} */ + var c2; + /** @type {!number} */ + var c3; + /** @type {!number} */ + var c4; + len = str.length; + i = 0; + out = [ ]; + while (i < len) { + do { + c1 = Binary._base64DecodeChars[str.charCodeAt(i++) & 0xff]; + } while (i < len && c1 == -1); + if (c1 == -1) { + break; + } + do { + c2 = Binary._base64DecodeChars[str.charCodeAt(i++) & 0xff]; + } while (i < len && c2 == -1); + if (c2 == -1) { + break; + } + out.push(c1 << 2 | (c2 & 0x30) >> 4); + do { + c3 = str.charCodeAt(i++) & 0xff; + if (c3 === 61) { + return Binary$_mergeCharCode$AI(out); + } + c3 = Binary._base64DecodeChars[c3]; + } while (i < len && c3 === -1); + if (c3 === -1) { + break; + } + out.push((c2 & 0XF) << 4 | (c3 & 0x3C) >> 2); + do { + c4 = str.charCodeAt(i++) & 0xff; + if (c4 === 61) { + return Binary$_mergeCharCode$AI(out); + } + c4 = (Binary._base64DecodeChars[c4] | 0); + } while (i < len && c4 === -1); + if (c4 === -1) { + break; + } + out.push((c3 & 0x03) << 6 | c4); + } + return Binary$_mergeCharCode$AI(out); +}; + +var Binary$base64decode$S = Binary.base64decode$S; + +/** + * class LoadedStringResult extends Object + * @constructor + */ +function LoadedStringResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedStringResult$SI(data, offset) { + /** @type {!number} */ + var strLength; + /** @type {Array.} */ + var bytes; + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {!number} */ + var offset$0; + this.result = ""; + this.offset = 0; + offset$0 = offset++; + strLength = data.charCodeAt(offset$0); + if (strLength > 32767) { + strLength = strLength - 32768; + bytes = [ ]; + for (i = 0; i < strLength; i += 2) { + code = data.charCodeAt(offset); + bytes.push(String.fromCharCode(code & 0x00ff)); + if (i !== strLength - 1) { + bytes.push(String.fromCharCode(code >>> 8)); + } + offset++; + } + this.result = bytes.join(''); + this.offset = offset; + } else { + this.result = data.slice(offset, offset + strLength); + this.offset = (offset + strLength | 0); + } +}; + +LoadedStringResult$SI.prototype = new LoadedStringResult; + +/** + * class LoadedStringListResult extends Object + * @constructor + */ +function LoadedStringListResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedStringListResult$SI(data, offset) { + /** @type {!number} */ + var length; + /** @type {!number} */ + var i; + /** @type {!number} */ + var strLength; + /** @type {!string} */ + var resultStr; + /** @type {Array.} */ + var bytes; + /** @type {!number} */ + var j; + /** @type {!number} */ + var code; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var offset$0; + this.offset = 0; + this.result = [ ]; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + length = result$0; + offset += 2; + for (i = 0; i < length; i++) { + offset$0 = offset++; + strLength = data.charCodeAt(offset$0); + if (strLength > 32767) { + strLength = strLength - 32768; + bytes = [ ]; + for (j = 0; j < strLength; j += 2) { + code = data.charCodeAt(offset); + bytes.push(String.fromCharCode(code & 0x00ff)); + if (j !== strLength - 1) { + bytes.push(String.fromCharCode(code >>> 8)); + } + offset++; + } + resultStr = bytes.join(''); + } else { + resultStr = data.slice(offset, offset + strLength); + offset = (offset + strLength | 0); + } + this.result.push(resultStr); + } + this.offset = offset; +}; + +LoadedStringListResult$SI.prototype = new LoadedStringListResult; + +/** + * class LoadedStringListMapResult extends Object + * @constructor + */ +function LoadedStringListMapResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedStringListMapResult$SI(data, offset) { + /** @type {!number} */ + var length; + /** @type {!number} */ + var i; + /** @type {LoadedStringResult} */ + var keyResult; + /** @type {LoadedStringListResult} */ + var valueResult; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var offset$0; + this.offset = 0; + this.result = ({ }); + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + length = result$0; + offset += 2; + for (i = 0; i < length; i++) { + keyResult = new LoadedStringResult$SI(data, offset); + offset$0 = keyResult.offset; + valueResult = new LoadedStringListResult$SI(data, offset$0); + this.result[keyResult.result] = valueResult.result; + offset = valueResult.offset; + } + this.offset = offset; +}; + +LoadedStringListMapResult$SI.prototype = new LoadedStringListMapResult; + +/** + * class LoadedNumberListResult extends Object + * @constructor + */ +function LoadedNumberListResult() { +} + +/** + * @constructor + * @param {!string} data + * @param {!number} offset + */ +function LoadedNumberListResult$SI(data, offset) { + /** @type {!number} */ + var resultLength; + /** @type {!number} */ + var originalOffset; + /** @type {Array.} */ + var result; + /** @type {!number} */ + var tag; + /** @type {!number} */ + var length; + /** @type {!number} */ + var i; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var value1$0; + this.result = null; + this.offset = 0; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + resultLength = result$0; + originalOffset = offset; + offset += 2; + result = [ ]; + while (result.length < resultLength) { + tag = data.charCodeAt(offset++); + if (tag >>> 15 === 1) { + value1$0 = resultLength - result.length; + length = (value1$0 <= 15 ? value1$0 : 15); + for (i = 0; i < length; i++) { + if (tag >>> i & 0x1) { + result.push(Binary$load32bitNumber$SI(data, offset)); + offset += 2; + } else { + result.push(0); + } + } + } else { + if (tag >>> 14 === 1) { + length = tag - 0x4000 + 1; + for (i = 0; i < length; i++) { + result.push(Binary$load32bitNumber$SI(data, offset)); + offset += 2; + } + } else { + length = tag + 1; + for (i = 0; i < length; i++) { + result.push(0); + } + } + } + } + this.result = result; + this.offset = offset; +}; + +LoadedNumberListResult$SI.prototype = new LoadedNumberListResult; + +/** + * class CompressionReport extends Object + * @constructor + */ +function CompressionReport() { +} + +/** + * @constructor + */ +function CompressionReport$() { + this.source = 0; + this.result = 0; +}; + +CompressionReport$.prototype = new CompressionReport; + +/** + * @param {CompressionReport} $this + * @param {!number} source + * @param {!number} result + */ +CompressionReport.add$LCompressionReport$II = function ($this, source, result) { + $this.source += source; + $this.result += result; +}; + +var CompressionReport$add$LCompressionReport$II = CompressionReport.add$LCompressionReport$II; + +/** + * @param {CompressionReport} $this + * @return {!number} + */ +CompressionReport.rate$LCompressionReport$ = function ($this) { + return (Math.round($this.result * 100.0 / $this.source) | 0); +}; + +var CompressionReport$rate$LCompressionReport$ = CompressionReport.rate$LCompressionReport$; + +/** + * class Query extends Object + * @constructor + */ +function Query() { +} + +/** + * @constructor + */ +function Query$() { + this.word = ''; + this.or = false; + this.not = false; + this.raw = false; +}; + +Query$.prototype = new Query; + +/** + * @return {!string} + */ +Query.prototype.toString = function () { + /** @type {Array.} */ + var result; + result = [ ]; + if (this.or) { + result.push("OR "); + } + if (this.not) { + result.push("-"); + } + if (this.raw) { + result.push('"', this.word, '"'); + } else { + result.push(this.word); + } + return result.join(''); +}; + +/** + * class QueryStringParser extends Object + * @constructor + */ +function QueryStringParser() { +} + +/** + * @constructor + */ +function QueryStringParser$() { + this.queries = [ ]; +}; + +QueryStringParser$.prototype = new QueryStringParser; + +/** + * @param {QueryStringParser} $this + * @param {!string} queryString + * @return {Array.} + */ +QueryStringParser.parse$LQueryStringParser$S = function ($this, queryString) { + /** @type {!boolean} */ + var nextOr; + /** @type {!boolean} */ + var nextNot; + /** @type {!number} */ + var currentWordStart; + /** @type {!number} */ + var status; + /** @type {RegExp} */ + var isSpace; + /** @type {!number} */ + var i; + /** @type {!string} */ + var ch; + /** @type {!string} */ + var word; + /** @type {Query} */ + var query; + nextOr = false; + nextNot = false; + currentWordStart = 0; + status = 0; + isSpace = /[\s\u3000]/; + for (i = 0; i < queryString.length; i++) { + ch = queryString.charAt(i); + switch (status) { + case 0: + if (! isSpace.test(ch)) { + if (ch === '-') { + nextNot = true; + } else { + if (ch === '"') { + currentWordStart = i + 1; + status = 2; + } else { + currentWordStart = i; + status = 1; + } + } + } else { + nextNot = false; + } + break; + case 1: + if (isSpace.test(ch)) { + word = queryString.slice(currentWordStart, i); + if (word === 'OR') { + nextOr = true; + } else { + query = new Query$(); + query.word = word; + query.or = nextOr; + query.not = nextNot; + $this.queries.push(query); + nextOr = false; + nextNot = false; + } + status = 0; + } + break; + case 2: + if (ch === '"') { + word = queryString.slice(currentWordStart, i); + query = new Query$(); + query.word = word; + query.or = nextOr; + query.not = nextNot; + query.raw = true; + $this.queries.push(query); + nextOr = false; + nextNot = false; + status = 0; + } + break; + } + } + switch (status) { + case 0: + break; + case 1: + query = new Query$(); + word = queryString.slice(currentWordStart, queryString.length); + if (word !== 'OR') { + query.word = word; + query.or = nextOr; + query.not = nextNot; + $this.queries.push(query); + } + break; + case 2: + query = new Query$(); + query.word = queryString.slice(currentWordStart, queryString.length); + query.or = nextOr; + query.not = nextNot; + query.raw = true; + $this.queries.push(query); + break; + } + return $this.queries; +}; + +var QueryStringParser$parse$LQueryStringParser$S = QueryStringParser.parse$LQueryStringParser$S; + +/** + * @param {QueryStringParser} $this + * @return {!string} + */ +QueryStringParser.highlight$LQueryStringParser$ = function ($this) { + /** @type {Array.} */ + var result; + /** @type {!number} */ + var i; + /** @type {Query} */ + var query; + result = [ ]; + for (i = 0; i < $this.queries.length; i++) { + query = $this.queries[i]; + if (! query.not) { + result.push("highlight=" + $__jsx_encodeURIComponent(query.word)); + } + } + return '?' + result.join('&'); +}; + +var QueryStringParser$highlight$LQueryStringParser$ = QueryStringParser.highlight$LQueryStringParser$; + +/** + * class Proposal extends Object + * @constructor + */ +function Proposal() { +} + +/** + * @constructor + * @param {!number} omit + * @param {!number} expect + */ +function Proposal$II(omit, expect) { + this.omit = omit; + this.expect = expect; +}; + +Proposal$II.prototype = new Proposal; + +/** + * class Position extends Object + * @constructor + */ +function Position() { +} + +/** + * @constructor + * @param {!string} word + * @param {!number} position + * @param {!boolean} stemmed + */ +function Position$SIB(word, position, stemmed) { + this.word = word; + this.position = position; + this.stemmed = stemmed; +}; + +Position$SIB.prototype = new Position; + +/** + * class SearchUnit extends Object + * @constructor + */ +function SearchUnit() { +} + +/** + * @constructor + * @param {!number} id + */ +function SearchUnit$I(id) { + this.positions = ({ }); + this.id = id; + this._size = 0; + this.score = 0; + this.startPosition = -1; +}; + +SearchUnit$I.prototype = new SearchUnit; + +/** + * @param {SearchUnit} $this + * @param {!string} word + * @param {!number} position + * @param {!boolean} stemmed + */ +SearchUnit.addPosition$LSearchUnit$SIB = function ($this, word, position, stemmed) { + /** @type {Position} */ + var positionObj; + positionObj = $this.positions[position + ""]; + if (! positionObj) { + $this._size++; + $this.positions[position + ""] = ({word: word, position: position, stemmed: stemmed}); + } else { + if (positionObj.word.length < word.length) { + positionObj.word = word; + } + positionObj.stemmed = positionObj.stemmed && stemmed; + } +}; + +var SearchUnit$addPosition$LSearchUnit$SIB = SearchUnit.addPosition$LSearchUnit$SIB; + +/** + * @param {SearchUnit} $this + * @param {!number} position + * @return {Position} + */ +SearchUnit.get$LSearchUnit$I = function ($this, position) { + return $this.positions[position + ""]; +}; + +var SearchUnit$get$LSearchUnit$I = SearchUnit.get$LSearchUnit$I; + +/** + * @param {SearchUnit} $this + * @return {!number} + */ +SearchUnit.size$LSearchUnit$ = function ($this) { + return $this._size; +}; + +var SearchUnit$size$LSearchUnit$ = SearchUnit.size$LSearchUnit$; + +/** + * @param {SearchUnit} $this + * @param {SearchUnit} rhs + */ +SearchUnit.merge$LSearchUnit$LSearchUnit$ = function ($this, rhs) { + /** @type {!string} */ + var position; + /** @type {Position} */ + var pos; + for (position in rhs.positions) { + pos = rhs.positions[position]; + SearchUnit$addPosition$LSearchUnit$SIB($this, pos.word, pos.position, pos.stemmed); + } +}; + +var SearchUnit$merge$LSearchUnit$LSearchUnit$ = SearchUnit.merge$LSearchUnit$LSearchUnit$; + +/** + * @param {SearchUnit} $this + * @return {Array.} + */ +SearchUnit.getPositions$LSearchUnit$ = function ($this) { + /** @type {Array.} */ + var result; + /** @type {!string} */ + var pos; + result = [ ]; + for (pos in $this.positions) { + result.push($this.positions[pos]); + } + result.sort((function (a, b) { + return a.position - b.position; + })); + return result; +}; + +var SearchUnit$getPositions$LSearchUnit$ = SearchUnit.getPositions$LSearchUnit$; + +/** + * class SingleResult extends Object + * @constructor + */ +function SingleResult() { +} + +/** + * @constructor + */ +function SingleResult$() { + this.units = [ ]; + this.unitIds = [ ]; + this.or = false; + this.not = false; + this.searchWord = ''; +}; + +SingleResult$.prototype = new SingleResult; + +/** + * @constructor + * @param {!string} searchWord + * @param {!boolean} or + * @param {!boolean} not + */ +function SingleResult$SBB(searchWord, or, not) { + this.units = [ ]; + this.unitIds = [ ]; + this.or = or; + this.not = not; + this.searchWord = searchWord; +}; + +SingleResult$SBB.prototype = new SingleResult; + +/** + * @param {SingleResult} $this + * @param {!number} unitId + * @return {SearchUnit} + */ +SingleResult.getSearchUnit$LSingleResult$I = function ($this, unitId) { + /** @type {!number} */ + var existing; + /** @type {SearchUnit} */ + var result; + existing = $this.unitIds.indexOf(unitId); + if (existing === -1) { + result = ({positions: ({ }), id: unitId, _size: 0, score: 0, startPosition: -1}); + $this.units.push(result); + $this.unitIds.push(unitId); + } else { + result = $this.units[existing]; + } + return result; +}; + +var SingleResult$getSearchUnit$LSingleResult$I = SingleResult.getSearchUnit$LSingleResult$I; + +/** + * @param {SingleResult} $this + * @param {SingleResult} rhs + * @return {SingleResult} + */ +SingleResult.merge$LSingleResult$LSingleResult$ = function ($this, rhs) { + /** @type {SingleResult} */ + var result; + result = ({units: [ ], unitIds: [ ], or: false, not: false, searchWord: ''}); + if (rhs.or) { + SingleResult$_orMerge$LSingleResult$LSingleResult$LSingleResult$($this, result, rhs); + } else { + if (rhs.not) { + SingleResult$_notMerge$LSingleResult$LSingleResult$LSingleResult$($this, result, rhs); + } else { + SingleResult$_andMerge$LSingleResult$LSingleResult$LSingleResult$($this, result, rhs); + } + } + return result; +}; + +var SingleResult$merge$LSingleResult$LSingleResult$ = SingleResult.merge$LSingleResult$LSingleResult$; + +/** + * @param {SingleResult} $this + * @return {!number} + */ +SingleResult.size$LSingleResult$ = function ($this) { + return ($this.units.length | 0); +}; + +var SingleResult$size$LSingleResult$ = SingleResult.size$LSingleResult$; + +/** + * @param {SingleResult} $this + * @param {SingleResult} result + * @param {SingleResult} rhs + */ +SingleResult._andMerge$LSingleResult$LSingleResult$LSingleResult$ = function ($this, result, rhs) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var id; + /** @type {SearchUnit} */ + var lhsSection; + for (i = 0; i < $this.unitIds.length; i++) { + id = $this.unitIds[i]; + if (rhs.unitIds.indexOf(id) !== -1) { + lhsSection = $this.units[i]; + result.unitIds.push(id); + result.units.push(lhsSection); + } + } +}; + +var SingleResult$_andMerge$LSingleResult$LSingleResult$LSingleResult$ = SingleResult._andMerge$LSingleResult$LSingleResult$LSingleResult$; + +/** + * @param {SingleResult} $this + * @param {SingleResult} result + * @param {SingleResult} rhs + */ +SingleResult._orMerge$LSingleResult$LSingleResult$LSingleResult$ = function ($this, result, rhs) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var id; + /** @type {SearchUnit} */ + var rhsSection; + /** @type {SearchUnit} */ + var lhsSection; + /** @type {Array.} */ + var unitIds$0; + /** @type {Array.} */ + var units$0; + result.unitIds = (unitIds$0 = $this.unitIds).slice(0, unitIds$0.length); + result.units = (units$0 = $this.units).slice(0, units$0.length); + for (i = 0; i < rhs.unitIds.length; i++) { + id = rhs.unitIds[i]; + rhsSection = rhs.units[i]; + if (result.unitIds.indexOf(id) !== -1) { + lhsSection = result.units[result.unitIds.indexOf(id)]; + SearchUnit$merge$LSearchUnit$LSearchUnit$(lhsSection, rhsSection); + } else { + result.unitIds.push(id); + result.units.push(rhsSection); + } + } +}; + +var SingleResult$_orMerge$LSingleResult$LSingleResult$LSingleResult$ = SingleResult._orMerge$LSingleResult$LSingleResult$LSingleResult$; + +/** + * @param {SingleResult} $this + * @param {SingleResult} result + * @param {SingleResult} rhs + */ +SingleResult._notMerge$LSingleResult$LSingleResult$LSingleResult$ = function ($this, result, rhs) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var id; + /** @type {SearchUnit} */ + var lhsSection; + for (i = 0; i < $this.unitIds.length; i++) { + id = $this.unitIds[i]; + if (rhs.unitIds.indexOf(id) === -1) { + lhsSection = $this.units[i]; + result.unitIds.push(id); + result.units.push(lhsSection); + } + } +}; + +var SingleResult$_notMerge$LSingleResult$LSingleResult$LSingleResult$ = SingleResult._notMerge$LSingleResult$LSingleResult$LSingleResult$; + +/** + * class SearchSummary extends Object + * @constructor + */ +function SearchSummary() { +} + +/** + * @constructor + */ +function SearchSummary$() { + this.sourceResults = [ ]; + this.result = null; + this.oktavia = null; +}; + +SearchSummary$.prototype = new SearchSummary; + +/** + * @constructor + * @param {Oktavia} oktavia + */ +function SearchSummary$LOktavia$(oktavia) { + this.sourceResults = [ ]; + this.result = null; + this.oktavia = oktavia; +}; + +SearchSummary$LOktavia$.prototype = new SearchSummary; + +/** + * @param {SearchSummary} $this + * @param {SingleResult} result + */ +SearchSummary.addQuery$LSearchSummary$LSingleResult$ = function ($this, result) { + $this.sourceResults.push(result); +}; + +var SearchSummary$addQuery$LSearchSummary$LSingleResult$ = SearchSummary.addQuery$LSearchSummary$LSingleResult$; + +/** + * @param {SearchSummary} $this + */ +SearchSummary.mergeResult$LSearchSummary$ = function ($this) { + $this.result = SearchSummary$mergeResult$LSearchSummary$ALSingleResult$($this, $this.sourceResults); +}; + +var SearchSummary$mergeResult$LSearchSummary$ = SearchSummary.mergeResult$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @param {Array.} results + * @return {SingleResult} + */ +SearchSummary.mergeResult$LSearchSummary$ALSingleResult$ = function ($this, results) { + /** @type {SingleResult} */ + var rhs; + /** @type {!number} */ + var i; + /** @type {!number} */ + var results$len$0; + rhs = results[0]; + for ((i = 1, results$len$0 = results.length); i < results$len$0; i++) { + rhs = SingleResult$merge$LSingleResult$LSingleResult$(rhs, results[i]); + } + return rhs; +}; + +var SearchSummary$mergeResult$LSearchSummary$ALSingleResult$ = SearchSummary.mergeResult$LSearchSummary$ALSingleResult$; + +/** + * @param {SearchSummary} $this + * @return {Array.} + */ +SearchSummary.getProposal$LSearchSummary$ = function ($this) { + /** @type {Array.} */ + var proposals; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var tmpSource; + /** @type {!number} */ + var j; + /** @type {SingleResult} */ + var result; + proposals = [ ]; + for (i = 0; i < $this.sourceResults.length; i++) { + tmpSource = [ ]; + for (j = 0; j < $this.sourceResults.length; j++) { + if (i !== j) { + tmpSource.push($this.sourceResults[j]); + } + } + result = SearchSummary$mergeResult$LSearchSummary$ALSingleResult$($this, tmpSource); + proposals.push(({omit: i, expect: result.units.length})); + } + proposals.sort((function (a, b) { + return b.expect - a.expect; + })); + return proposals; +}; + +var SearchSummary$getProposal$LSearchSummary$ = SearchSummary.getProposal$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @return {Array.} + */ +SearchSummary.getSortedResult$LSearchSummary$ = function ($this) { + /** @type {Array.} */ + var result; + /** @type {Array.} */ + var units$0; + result = (units$0 = $this.result.units).slice(0, units$0.length); + result.sort((function (a, b) { + return b.score - a.score; + })); + return result; +}; + +var SearchSummary$getSortedResult$LSearchSummary$ = SearchSummary.getSortedResult$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @return {!number} + */ +SearchSummary.size$LSearchSummary$ = function ($this) { + /** @type {SingleResult} */ + var this$0; + this$0 = $this.result; + return (this$0.units.length | 0); +}; + +var SearchSummary$size$LSearchSummary$ = SearchSummary.size$LSearchSummary$; + +/** + * @param {SearchSummary} $this + * @param {SingleResult} result + */ +SearchSummary.add$LSearchSummary$LSingleResult$ = function ($this, result) { + $this.sourceResults.push(result); +}; + +var SearchSummary$add$LSearchSummary$LSingleResult$ = SearchSummary.add$LSearchSummary$LSingleResult$; + +/** + * class Style extends Object + * @constructor + */ +function Style() { +} + +/** + * @constructor + * @param {!string} mode + */ +function Style$S(mode) { + this.styles = null; + this.escapeHTML = false; + switch (mode) { + case 'console': + this.styles = Style.console; + break; + case 'html': + this.styles = Style.html; + break; + case 'ignore': + this.styles = Style.ignore; + break; + default: + this.styles = Style.ignore; + break; + } + this.escapeHTML = mode === 'html'; +}; + +Style$S.prototype = new Style; + +/** + * @param {!string} source + * @return {!string} + */ +Style.prototype.convert$S = function (source) { + /** @type {_HTMLHandler} */ + var handler; + /** @type {SAXParser} */ + var parser; + handler = new _HTMLHandler$HASB(this.styles, this.escapeHTML); + parser = new SAXParser$LSAXHandler$(handler); + parser.parse$S(source); + return handler.text.join(''); +}; + +/** + * class Stemmer + * @constructor + */ +function Stemmer() { +} + +Stemmer.prototype.$__jsx_implements_Stemmer = true; + +/** + * @constructor + */ +function Stemmer$() { +}; + +Stemmer$.prototype = new Stemmer; + +/** + * class Metadata extends Object + * @constructor + */ +function Metadata() { +} + +/** + * @constructor + * @param {Oktavia} parent + */ +function Metadata$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); +}; + +Metadata$LOktavia$.prototype = new Metadata; + +/** + * @return {!number} + */ +Metadata.prototype._size$ = function () { + /** @type {BitVector} */ + var this$0; + /** @type {!number} */ + var i$0; + /** @type {BitVector} */ + var _bitVector$0; + this$0 = _bitVector$0 = this._bitVector; + i$0 = _bitVector$0._size; + return this$0.rank$IB(i$0, true); +}; + +/** + * @param {!number} index + * @return {!string} + */ +Metadata.prototype.getContent$I = function (index) { + /** @type {!number} */ + var startPosition; + /** @type {!number} */ + var length; + if (index < 0 || this._size$() <= index) { + throw new Error("Section.getContent() : range error " + (index + "")); + } + startPosition = 0; + if (index > 0) { + startPosition = this._bitVector.select$I(index - 1) + 1; + } + length = this._bitVector.select$I(index) - startPosition + 1; + return this._parent._getSubstring$II(startPosition, length); +}; + +/** + * @param {!number} index + * @return {!number} + */ +Metadata.prototype.getStartPosition$I = function (index) { + /** @type {!number} */ + var startPosition; + if (index < 0 || this._size$() <= index) { + throw new Error("Section.getContent() : range error " + (index + "")); + } + startPosition = 0; + if (index > 0) { + startPosition = this._bitVector.select$I(index - 1) + 1; + } + return (startPosition | 0); +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Metadata.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { +}; + +/** + * @param {!number} index + * @return {!string} + */ +Metadata.prototype.getInformation$I = function (index) { + return ''; +}; + +/** + */ +Metadata.prototype._build$ = function () { + this._bitVector.build$(); +}; + +/** + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Metadata.prototype._load$SSI = function (name, data, offset) { + offset = this._bitVector.load$SI(data, offset); + this._parent._metadataLabels.push(name); + this._parent._metadatas[name] = this; + return offset; +}; + +/** + * @return {!string} + */ +Metadata.prototype._dump$ = function () { + /** @type {BitVector} */ + var this$0; + /** @type {Array.} */ + var contents$0; + this$0 = this._bitVector; + contents$0 = [ ]; + contents$0.push(Binary$dump32bitNumber$N(this$0._size)); + contents$0.push(Binary$dump32bitNumberList$AN(this$0._v)); + return contents$0.join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Metadata.prototype._dump$LCompressionReport$ = function (report) { + /** @type {BitVector} */ + var this$0; + /** @type {Array.} */ + var contents$0; + this$0 = this._bitVector; + contents$0 = [ ]; + contents$0.push(Binary$dump32bitNumber$N(this$0._size)); + CompressionReport$add$LCompressionReport$II(report, 2, 2); + contents$0.push(Binary$dump32bitNumberList$ANLCompressionReport$(this$0._v, report)); + return contents$0.join(''); +}; + +/** + * class Section extends Metadata + * @constructor + */ +function Section() { +} + +Section.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + */ +function Section$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); + this._names = [ ]; +}; + +Section$LOktavia$.prototype = new Section; + +/** + * @param {!string} name + */ +Section.prototype.setTail$S = function (name) { + /** @type {!number} */ + var index$0; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + this$0 = this._parent; + this$0$0 = this$0._fmindex; + index$0 = this$0$0._substr.length; + this._names.push(name); + this._bitVector.set$I(index$0 - 1); +}; + +/** + * @param {!string} name + * @param {!number} index + */ +Section.prototype.setTail$SI = function (name, index) { + this._names.push(name); + this._bitVector.set$I(index - 1); +}; + +/** + * @return {!number} + */ +Section.prototype.size$ = function () { + return (this._names.length | 0); +}; + +/** + * @param {!number} position + * @return {!number} + */ +Section.prototype.getSectionIndex$I = function (position) { + /** @type {BitVector} */ + var this$0; + if (position < 0 || this._bitVector.size$() <= position) { + throw new Error("Section.getSectionIndex() : range error " + (position + "")); + } + this$0 = this._bitVector; + return this$0.rank$IB(position, true); +}; + +/** + * @param {!number} index + * @return {!string} + */ +Section.prototype.getName$I = function (index) { + if (index < 0 || this._names.length <= index) { + throw new Error("Section.getName() : range error"); + } + return this._names[index]; +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Section.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var position; + /** @type {!number} */ + var index; + /** @type {SearchUnit} */ + var unit; + for (i = 0; i < positions.length; i++) { + position = positions[i]; + index = this.getSectionIndex$I(position); + unit = SingleResult$getSearchUnit$LSingleResult$I(result, index); + if (unit.startPosition < 0) { + unit.startPosition = this.getStartPosition$I(index); + } + SearchUnit$addPosition$LSearchUnit$SIB(unit, word, position - unit.startPosition, stemmed); + } +}; + +/** + * @param {!number} index + * @return {!string} + */ +Section.prototype.getInformation$I = function (index) { + return this.getName$I(index); +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Section._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {LoadedStringListResult} */ + var strs; + /** @type {Section} */ + var section; + /** @type {!number} */ + var offset$0; + strs = new LoadedStringListResult$SI(data, offset); + section = new Section$LOktavia$(parent); + section._names = strs.result; + offset$0 = strs.offset; + offset$0 = section._bitVector.load$SI(data, offset$0); + section._parent._metadataLabels.push(name); + section._parent._metadatas[name] = section; + return offset$0; +}; + +var Section$_load$LOktavia$SSI = Section._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Section.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(0), Binary$dumpStringList$AS(this._names), Metadata.prototype._dump$.call(this) ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Section.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(0), Binary$dumpStringList$ASLCompressionReport$(this._names, report), Metadata.prototype._dump$LCompressionReport$.call(this, report) ].join(''); +}; + +/** + * class Splitter extends Metadata + * @constructor + */ +function Splitter() { +} + +Splitter.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + */ +function Splitter$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); + this.name = null; +}; + +Splitter$LOktavia$.prototype = new Splitter; + +/** + * @constructor + * @param {Oktavia} parent + * @param {!string} name + */ +function Splitter$LOktavia$S(parent, name) { + this._parent = parent; + this._bitVector = new BitVector$(); + this.name = name; +}; + +Splitter$LOktavia$S.prototype = new Splitter; + +/** + * @return {!number} + */ +Splitter.prototype.size$ = function () { + /** @type {BitVector} */ + var this$0$0; + /** @type {!number} */ + var i$0$0; + /** @type {BitVector} */ + var _bitVector$0; + this$0$0 = _bitVector$0 = this._bitVector; + i$0$0 = _bitVector$0._size; + return this$0$0.rank$IB(i$0$0, true); +}; + +/** + */ +Splitter.prototype.split$ = function () { + /** @type {!number} */ + var index$0; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + this$0 = this._parent; + this$0$0 = this$0._fmindex; + index$0 = this$0$0._substr.length; + this._bitVector.set$I(index$0 - 1); +}; + +/** + * @param {!number} index + */ +Splitter.prototype.split$I = function (index) { + this._bitVector.set$I(index - 1); +}; + +/** + * @param {!number} position + * @return {!number} + */ +Splitter.prototype.getIndex$I = function (position) { + /** @type {BitVector} */ + var this$0; + if (position < 0 || this._bitVector.size$() <= position) { + throw new Error("Section.getSectionIndex() : range error"); + } + this$0 = this._bitVector; + return this$0.rank$IB(position, true); +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Splitter.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { + /** @type {!number} */ + var i; + /** @type {undefined|!number} */ + var position; + /** @type {!number} */ + var index; + /** @type {SearchUnit} */ + var unit; + for (i = 0; i < positions.length; i++) { + position = positions[i]; + index = this.getIndex$I(position); + unit = SingleResult$getSearchUnit$LSingleResult$I(result, index); + if (unit.startPosition < 0) { + unit.startPosition = this.getStartPosition$I(index); + } + SearchUnit$addPosition$LSearchUnit$SIB(unit, word, position - unit.startPosition, stemmed); + } +}; + +/** + * @param {!number} index + * @return {!string} + */ +Splitter.prototype.getInformation$I = function (index) { + return (this.name != null ? this.name + (index + 1 + "") : ''); +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Splitter._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {Splitter} */ + var section; + section = new Splitter$LOktavia$(parent); + offset = section._bitVector.load$SI(data, offset); + section._parent._metadataLabels.push(name); + section._parent._metadatas[name] = section; + return offset; +}; + +var Splitter$_load$LOktavia$SSI = Splitter._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Splitter.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(1), Metadata.prototype._dump$.call(this) ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Splitter.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(1), Metadata.prototype._dump$LCompressionReport$.call(this, report) ].join(''); +}; + +/** + * class Table extends Metadata + * @constructor + */ +function Table() { +} + +Table.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + * @param {Array.} headers + */ +function Table$LOktavia$AS(parent, headers) { + this._parent = parent; + this._bitVector = new BitVector$(); + this._headers = headers; + this._columnTails = new BitVector$(); +}; + +Table$LOktavia$AS.prototype = new Table; + +/** + * @return {!number} + */ +Table.prototype.rowSize$ = function () { + /** @type {BitVector} */ + var this$0$0; + /** @type {!number} */ + var i$0$0; + /** @type {BitVector} */ + var _bitVector$0; + this$0$0 = _bitVector$0 = this._bitVector; + i$0$0 = _bitVector$0._size; + return this$0$0.rank$IB(i$0$0, true); +}; + +/** + * @return {!number} + */ +Table.prototype.columnSize$ = function () { + return (this._headers.length | 0); +}; + +/** + */ +Table.prototype.setColumnTail$ = function () { + /** @type {!number} */ + var index; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + /** @type {Oktavia} */ + var _parent$0; + this$0 = _parent$0 = this._parent; + this$0$0 = this$0._fmindex; + index = this$0$0._substr.length; + _parent$0._fmindex.push$S(Oktavia.eob); + this._columnTails.set$I(index - 1); +}; + +/** + */ +Table.prototype.setRowTail$ = function () { + /** @type {!number} */ + var index; + /** @type {Oktavia} */ + var this$0; + /** @type {FMIndex} */ + var this$0$0; + this$0 = this._parent; + this$0$0 = this$0._fmindex; + index = this$0$0._substr.length; + this._bitVector.set$I(index - 1); +}; + +/** + * @param {!number} position + * @return {Array.} + */ +Table.prototype.getCell$I = function (position) { + /** @type {!number} */ + var row; + /** @type {!number} */ + var currentColumn; + /** @type {!number} */ + var lastRowColumn; + /** @type {!number} */ + var startPosition; + /** @type {Array.} */ + var result; + /** @type {BitVector} */ + var this$0; + /** @type {BitVector} */ + var this$1; + if (position < 0 || this._bitVector.size$() <= position) { + throw new Error("Section.getSectionIndex() : range error " + (position + "")); + } + this$0 = this._bitVector; + row = this$0.rank$IB(position, true); + this$1 = this._columnTails; + currentColumn = this$1.rank$IB(position, true); + lastRowColumn = 0; + if (row > 0) { + startPosition = this._bitVector.select$I(row - 1) + 1; + lastRowColumn = this._columnTails.rank$I(startPosition); + } + result = [ row, currentColumn - lastRowColumn ]; + return result; +}; + +/** + * @param {!number} rowIndex + * @return {Object.} + */ +Table.prototype.getRowContent$I = function (rowIndex) { + /** @type {!string} */ + var content; + /** @type {Array.} */ + var values; + /** @type {Object.} */ + var result; + /** @type {!number} */ + var i; + content = this.getContent$I(rowIndex); + values = content.split(Oktavia.eob, this._headers.length); + result = ({ }); + for (i in this._headers) { + if (i < values.length) { + result[this._headers[i]] = values[i]; + } else { + result[this._headers[i]] = ''; + } + } + return result; +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Table.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { +}; + +/** + * @param {!number} index + * @return {!string} + */ +Table.prototype.getInformation$I = function (index) { + return ''; +}; + +/** + */ +Table.prototype._build$ = function () { + this._bitVector.build$(); + this._columnTails.build$(); +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Table._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {LoadedStringListResult} */ + var strs; + /** @type {Table} */ + var table; + /** @type {!number} */ + var offset$0; + strs = new LoadedStringListResult$SI(data, offset); + table = new Table$LOktavia$AS(parent, strs.result); + offset$0 = strs.offset; + offset$0 = table._bitVector.load$SI(data, offset$0); + table._parent._metadataLabels.push(name); + table._parent._metadatas[name] = table; + offset = offset$0; + return table._columnTails.load$SI(data, offset$0); +}; + +var Table$_load$LOktavia$SSI = Table._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Table.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(2), Binary$dumpStringList$AS(this._headers), Metadata.prototype._dump$.call(this), this._columnTails.dump$() ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Table.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(2), Binary$dumpStringList$ASLCompressionReport$(this._headers, report), Metadata.prototype._dump$LCompressionReport$.call(this, report), this._columnTails.dump$LCompressionReport$(report) ].join(''); +}; + +/** + * class Block extends Metadata + * @constructor + */ +function Block() { +} + +Block.prototype = new Metadata; +/** + * @constructor + * @param {Oktavia} parent + */ +function Block$LOktavia$(parent) { + this._parent = parent; + this._bitVector = new BitVector$(); + this._names = [ ]; + this._start = false; +}; + +Block$LOktavia$.prototype = new Block; + +/** + * @param {!string} blockName + */ +Block.prototype.startBlock$S = function (blockName) { + this.startBlock$SI(blockName, this._parent.contentSize$()); +}; + +/** + * @param {!string} blockName + * @param {!number} index + */ +Block.prototype.startBlock$SI = function (blockName, index) { + if (this._start) { + throw new Error('Splitter `' + this._names[this._names.length - 1] + '` is not closed'); + } + this._start = true; + this._names.push(blockName); + this._bitVector.set$I(index - 1); +}; + +/** + */ +Block.prototype.endBlock$ = function () { + this.endBlock$I(this._parent.contentSize$()); +}; + +/** + * @param {!number} index + */ +Block.prototype.endBlock$I = function (index) { + if (! this._start) { + throw new Error('Splitter is not started'); + } + this._start = false; + this._bitVector.set$I(index - 1); +}; + +/** + * @return {!number} + */ +Block.prototype.size$ = function () { + return (this._names.length | 0); +}; + +/** + * @param {!number} position + * @return {!number} + */ +Block.prototype.blockIndex$I = function (position) { + /** @type {!number} */ + var result; + /** @type {BitVector} */ + var this$0; + if (position < 0 || this._parent._fmindex.size$() - 1 <= position) { + throw new Error("Block.blockIndex() : range error " + (position + "")); + } + if (position >= this._bitVector.size$()) { + position = (this._bitVector.size$() - 1 | 0); + result = (this._bitVector.rank$I(position) + 1 | 0); + } else { + this$0 = this._bitVector; + result = this$0.rank$IB(position, true); + } + return result; +}; + +/** + * @param {!number} position + * @return {!boolean} + */ +Block.prototype.inBlock$I = function (position) { + /** @type {!number} */ + var blockIndex; + blockIndex = this.blockIndex$I(position); + return blockIndex % 2 !== 0; +}; + +/** + * @param {!number} position + * @return {!string} + */ +Block.prototype.getBlockContent$I = function (position) { + /** @type {!number} */ + var blockIndex; + /** @type {!string} */ + var result; + blockIndex = this.blockIndex$I(position); + if (blockIndex % 2 !== 0) { + result = this.getContent$I(blockIndex); + } else { + result = ''; + } + return result; +}; + +/** + * @param {!number} position + * @return {!string} + */ +Block.prototype.getBlockName$I = function (position) { + /** @type {!number} */ + var blockIndex; + /** @type {!string} */ + var result; + blockIndex = this.blockIndex$I(position); + if (blockIndex % 2 !== 0) { + result = this._names[blockIndex >>> 1]; + } else { + result = ''; + } + return result; +}; + +/** + * @param {SingleResult} result + * @param {Array.} positions + * @param {!string} word + * @param {!boolean} stemmed + */ +Block.prototype.grouping$LSingleResult$AISB = function (result, positions, word, stemmed) { +}; + +/** + * @param {!number} index + * @return {!string} + */ +Block.prototype.getInformation$I = function (index) { + return ''; +}; + +/** + * @param {Oktavia} parent + * @param {!string} name + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +Block._load$LOktavia$SSI = function (parent, name, data, offset) { + /** @type {LoadedStringListResult} */ + var strs; + /** @type {Block} */ + var block; + /** @type {!number} */ + var offset$0; + strs = new LoadedStringListResult$SI(data, offset); + block = new Block$LOktavia$(parent); + block._names = strs.result; + offset$0 = strs.offset; + offset$0 = block._bitVector.load$SI(data, offset$0); + block._parent._metadataLabels.push(name); + block._parent._metadatas[name] = block; + return offset$0; +}; + +var Block$_load$LOktavia$SSI = Block._load$LOktavia$SSI; + +/** + * @return {!string} + */ +Block.prototype._dump$ = function () { + return [ Binary$dump16bitNumber$I(3), Binary$dumpStringList$AS(this._names), Metadata.prototype._dump$.call(this) ].join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +Block.prototype._dump$LCompressionReport$ = function (report) { + CompressionReport$add$LCompressionReport$II(report, 1, 1); + return [ Binary$dump16bitNumber$I(3), Binary$dumpStringList$ASLCompressionReport$(this._names, report), Metadata.prototype._dump$LCompressionReport$.call(this, report) ].join(''); +}; + +/** + * class FMIndex extends Object + * @constructor + */ +function FMIndex() { +} + +/** + * @constructor + */ +function FMIndex$() { + /** @type {Array.} */ + var _rlt$0; + this._ssize = 0; + (this._ddic = 0, this._head = 0); + this._substr = ""; + this._sv = new WaveletMatrix$(); + this._posdic = [ ]; + this._idic = [ ]; + _rlt$0 = this._rlt = [ ]; + _rlt$0.length = 65536; +}; + +FMIndex$.prototype = new FMIndex; + +/** + */ +FMIndex.prototype.clear$ = function () { + /** @type {WaveletMatrix} */ + var this$0; + this$0 = this._sv; + this$0._bv.length = 0; + this$0._seps.length = 0; + this$0._size = 0; + this._posdic.length = 0; + this._idic.length = 0; + this._ddic = 0; + this._head = 0; + this._substr = ""; +}; + +/** + * @return {!number} + */ +FMIndex.prototype.size$ = function () { + /** @type {WaveletMatrix} */ + var this$0; + this$0 = this._sv; + return this$0._size; +}; + +/** + * @return {!number} + */ +FMIndex.prototype.contentSize$ = function () { + return this._substr.length; +}; + +/** + * @param {!string} key + * @return {!number} + */ +FMIndex.prototype.getRows$S = function (key) { + /** @type {Array.} */ + var pos; + pos = [ ]; + return this.getRows$SAI(key, pos); +}; + +/** + * @param {!string} key + * @param {Array.} pos + * @return {!number} + */ +FMIndex.prototype.getRows$SAI = function (key, pos) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {!number} */ + var first; + /** @type {undefined|!number} */ + var last; + /** @type {!number} */ + var c; + /** @type {Array.} */ + var _rlt$0; + i = key.length - 1; + code = key.charCodeAt(i); + first = (_rlt$0 = this._rlt)[code] + 1; + last = _rlt$0[code + 1]; + while (first <= last) { + if (i === 0) { + pos[0] = (-- first | 0); + pos[1] = -- last; + return (last - first + 1 | 0); + } + i--; + c = key.charCodeAt(i); + first = this._rlt[c] + this._sv.rank$II(first - 1, c) + 1; + last = this._rlt[c] + this._sv.rank$II(last, c); + } + return 0; +}; + +/** + * @param {!number} i + * @return {!number} + */ +FMIndex.prototype.getPosition$I = function (i) { + /** @type {!number} */ + var pos; + /** @type {!number} */ + var c; + if (i >= this.size$()) { + throw new Error("FMIndex.getPosition() : range error"); + } + pos = 0; + while (i !== this._head) { + if (i % this._ddic === 0) { + pos += this._posdic[i / this._ddic] + 1; + break; + } + c = this._sv.get$I(i); + i = this._rlt[c] + this._sv.rank$II(i, c); + pos++; + } + return (pos % this.size$() | 0); +}; + +/** + * @param {!number} pos + * @param {!number} len + * @return {!string} + */ +FMIndex.prototype.getSubstring$II = function (pos, len) { + /** @type {!number} */ + var pos_end; + /** @type {!number} */ + var pos_tmp; + /** @type {!number} */ + var i; + /** @type {!number} */ + var pos_idic; + /** @type {!string} */ + var substr; + /** @type {!number} */ + var c; + /** @type {!number} */ + var _ddic$0; + if (pos >= this.size$()) { + throw new Error("FMIndex.getSubstring() : range error"); + } + pos_end = Math.min(pos + len, this.size$()); + pos_tmp = this.size$() - 1; + i = this._head; + pos_idic = Math.floor((pos_end + (_ddic$0 = this._ddic) - 2) / _ddic$0); + if (pos_idic < this._idic.length) { + pos_tmp = pos_idic * this._ddic; + i = this._idic[pos_idic]; + } + substr = ""; + while (pos_tmp >= pos) { + c = this._sv.get$I(i); + i = this._rlt[c] + this._sv.rank$II(i, c); + if (pos_tmp < pos_end) { + substr = String.fromCharCode(c) + substr; + } + if (pos_tmp === 0) { + break; + } + pos_tmp--; + } + return substr; +}; + +/** + */ +FMIndex.prototype.build$ = function () { + this.build$SIIB(String.fromCharCode(0), 65535, 20, false); +}; + +/** + * @param {!string} end_marker + * @param {!number} ddic + * @param {!boolean} verbose + */ +FMIndex.prototype.build$SIB = function (end_marker, ddic, verbose) { + this.build$SIIB(end_marker, 65535, ddic, verbose); +}; + +/** + * @param {!string} end_marker + * @param {!number} maxChar + * @param {!number} ddic + * @param {!boolean} verbose + */ +FMIndex.prototype.build$SIIB = function (end_marker, maxChar, ddic, verbose) { + /** @type {BurrowsWheelerTransform} */ + var b; + /** @type {!string} */ + var s; + /** @type {!number} */ + var c; + /** @type {!string} */ + var str$0; + /** @type {WaveletMatrix} */ + var this$0; + /** @type {!string} */ + var _str$0; + /** @type {Array.} */ + var _suffixarray$0; + if (verbose) { + console.time("building burrows-wheeler transform"); + } + this._substr += end_marker; + b = ({_str: "", _size: 0, _head: 0, _suffixarray: [ ]}); + str$0 = this._substr; + _str$0 = b._str = str$0; + b._size = _str$0.length; + _suffixarray$0 = b._suffixarray = SAIS$make$S(str$0); + b._head = (_suffixarray$0.indexOf(0) | 0); + s = BurrowsWheelerTransform$get$LBurrowsWheelerTransform$(b); + this._ssize = s.length; + this._head = b._head; + b._str = ""; + b._size = 0; + b._head = 0; + b._suffixarray.length = 0; + this._substr = ""; + if (verbose) { + console.timeEnd("building burrows-wheeler transform"); + } + if (verbose) { + console.time("building wavelet matrix"); + } + this$0 = this._sv; + this$0._bitsize = (Math.ceil(Math.log(maxChar) / 0.6931471805599453) | 0); + if (verbose) { + console.log(" maxCharCode: ", maxChar); + console.log(" bitSize: ", this._sv.bitsize$()); + } + this._sv.build$S(s); + if (verbose) { + console.timeEnd("building wavelet matrix"); + } + if (verbose) { + console.time("caching rank less than"); + } + for (c = 0; c < maxChar; c++) { + this._rlt[c] = this._sv.rank_less_than$II(this._sv.size$(), c); + } + if (verbose) { + console.timeEnd("caching rank less than"); + } + this._ddic = ddic; + if (verbose) { + console.time("building dictionaries"); + } + this._buildDictionaries$(); + if (verbose) { + console.timeEnd("building dictionaries"); + console.log(''); + } +}; + +/** + */ +FMIndex.prototype._buildDictionaries$ = function () { + /** @type {!number} */ + var i; + /** @type {!number} */ + var pos; + /** @type {!number} */ + var c; + for (i = 0; i < this._ssize / this._ddic + 1; i++) { + this._posdic.push(0); + this._idic.push(0); + } + i = this._head; + pos = this.size$() - 1; + do { + if (i % this._ddic === 0) { + this._posdic[Math.floor(i / this._ddic)] = (pos | 0); + } + if (pos % this._ddic === 0) { + this._idic[Math.floor(pos / this._ddic)] = (i | 0); + } + c = this._sv.get$I(i); + i = this._rlt[c] + this._sv.rank$II(i, c); + pos--; + } while (i !== this._head); +}; + +/** + * @param {!string} doc + */ +FMIndex.prototype.push$S = function (doc) { + if (doc.length <= 0) { + throw new Error("FMIndex::push(): empty string"); + } + this._substr += doc; +}; + +/** + * @param {!string} keyword + * @return {Array.} + */ +FMIndex.prototype.search$S = function (keyword) { + /** @type {Array.} */ + var result; + /** @type {Array.} */ + var position; + /** @type {!number} */ + var rows; + /** @type {undefined|!number} */ + var first; + /** @type {undefined|!number} */ + var last; + /** @type {undefined|!number} */ + var i; + result = [ ]; + position = [ ]; + rows = this.getRows$SAI(keyword, position); + if (rows > 0) { + first = position[0]; + last = position[1]; + for (i = first; i <= last; i++) { + result.push(this.getPosition$I(i)); + } + } + return result; +}; + +/** + * @return {!string} + */ +FMIndex.prototype.dump$ = function () { + return this.dump$B(false); +}; + +/** + * @param {!boolean} verbose + * @return {!string} + */ +FMIndex.prototype.dump$B = function (verbose) { + /** @type {Array.} */ + var contents; + /** @type {CompressionReport} */ + var report; + /** @type {!number} */ + var i; + contents = [ ]; + report = ({source: 0, result: 0}); + contents.push(Binary$dump32bitNumber$N(this._ddic)); + contents.push(Binary$dump32bitNumber$N(this._ssize)); + contents.push(Binary$dump32bitNumber$N(this._head)); + CompressionReport$add$LCompressionReport$II(report, 6, 6); + contents.push(this._sv.dump$LCompressionReport$(report)); + if (verbose) { + console.log("Serializing FM-index"); + console.log(' Wavelet Matrix: ' + (contents[3].length * 2 + "") + ' bytes (' + (Math.round(report.result * 100.0 / report.source) + "") + '%)'); + } + contents.push(Binary$dump32bitNumber$N(this._posdic.length)); + for (i in this._posdic) { + contents.push(Binary$dump32bitNumber$N(this._posdic[i])); + } + for (i in this._idic) { + contents.push(Binary$dump32bitNumber$N(this._idic[i])); + } + if (verbose) { + console.log(' Dictionary Cache: ' + (this._idic.length * 16 + "") + ' bytes'); + } + return contents.join(""); +}; + +/** + * @param {!string} data + * @return {!number} + */ +FMIndex.prototype.load$S = function (data) { + return this.load$SI(data, 0); +}; + +/** + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +FMIndex.prototype.load$SI = function (data, offset) { + /** @type {!number} */ + var maxChar; + /** @type {!number} */ + var c; + /** @type {!number} */ + var size; + /** @type {!number} */ + var i; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var result$1; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + this._ddic = (result$0 | 0); + this._ssize = (Binary$load32bitNumber$SI(data, offset + 2) | 0); + this._head = (Binary$load32bitNumber$SI(data, offset + 4) | 0); + offset = this._sv.load$SI(data, offset + 6); + maxChar = Math.pow(2, this._sv.bitsize$()); + for (c = 0; c < maxChar; c++) { + this._rlt[c] = this._sv.rank_less_than$II(this._sv.size$(), c); + } + result$1 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + size = result$1; + offset += 2; + for (i = 0; i < size; (i++, offset += 2)) { + this._posdic.push(Binary$load32bitNumber$SI(data, offset)); + } + for (i = 0; i < size; (i++, offset += 2)) { + this._idic.push(Binary$load32bitNumber$SI(data, offset)); + } + return offset; +}; + +/** + * class Tag extends Object + * @constructor + */ +function Tag() { +} + +/** + * @constructor + * @param {!string} name + */ +function Tag$S(name) { + this.name = name; + this.attributes = ({ }); + this.isSelfClosing = false; +}; + +Tag$S.prototype = new Tag; + +/** + * class _Common extends Object + * @constructor + */ +function _Common() { +} + +/** + * @constructor + */ +function _Common$() { +}; + +_Common$.prototype = new _Common; + +/** + * class _State extends Object + * @constructor + */ +function _State() { +} + +/** + * @constructor + */ +function _State$() { +}; + +_State$.prototype = new _State; + +/** + * class SAXHandler extends Object + * @constructor + */ +function SAXHandler() { +} + +/** + * @constructor + */ +function SAXHandler$() { + this.position = 0; + this.column = 0; + this.line = 0; +}; + +SAXHandler$.prototype = new SAXHandler; + +/** + * @param {Error} error + */ +SAXHandler.prototype.onerror$LError$ = function (error) { +}; + +/** + * @param {!string} text + */ +SAXHandler.prototype.ontext$S = function (text) { +}; + +/** + * @param {!string} doctype + */ +SAXHandler.prototype.ondoctype$S = function (doctype) { +}; + +/** + * @param {!string} name + * @param {!string} body + */ +SAXHandler.prototype.onprocessinginstruction$SS = function (name, body) { +}; + +/** + * @param {!string} sgmlDecl + */ +SAXHandler.prototype.onsgmldeclaration$S = function (sgmlDecl) { +}; + +/** + * @param {!string} tagname + * @param {Object.} attributes + */ +SAXHandler.prototype.onopentag$SHS = function (tagname, attributes) { +}; + +/** + * @param {!string} tagname + */ +SAXHandler.prototype.onclosetag$S = function (tagname) { +}; + +/** + * @param {!string} name + * @param {!string} value + */ +SAXHandler.prototype.onattribute$SS = function (name, value) { +}; + +/** + * @param {!string} comment + */ +SAXHandler.prototype.oncomment$S = function (comment) { +}; + +/** + */ +SAXHandler.prototype.onopencdata$ = function () { +}; + +/** + * @param {!string} cdata + */ +SAXHandler.prototype.oncdata$S = function (cdata) { +}; + +/** + */ +SAXHandler.prototype.onclosecdata$ = function () { +}; + +/** + */ +SAXHandler.prototype.onend$ = function () { +}; + +/** + */ +SAXHandler.prototype.onready$ = function () { +}; + +/** + * @param {!string} script + */ +SAXHandler.prototype.onscript$S = function (script) { +}; + +/** + * class _HTMLHandler extends SAXHandler + * @constructor + */ +function _HTMLHandler() { +} + +_HTMLHandler.prototype = new SAXHandler; +/** + * @constructor + * @param {Object.>} styles + * @param {!boolean} escape + */ +function _HTMLHandler$HASB(styles, escape) { + this.position = 0; + this.column = 0; + this.line = 0; + this.text = [ ]; + this.escape = escape; + this.styles = styles; +}; + +_HTMLHandler$HASB.prototype = new _HTMLHandler; + +/** + * @param {!string} str + * @return {!string} + */ +_HTMLHandler.escapeHTML$S = function (str) { + return str.replace(/\n/g, "
").replace(/&/g, "&").replace(/"/g, """).replace(//g, ">"); +}; + +var _HTMLHandler$escapeHTML$S = _HTMLHandler.escapeHTML$S; + +/** + * @param {!string} tagname + * @param {Object.} attributes + */ +_HTMLHandler.prototype.onopentag$SHS = function (tagname, attributes) { + this.text.push(this.styles[tagname][0]); +}; + +/** + * @param {!string} tagname + */ +_HTMLHandler.prototype.onclosetag$S = function (tagname) { + this.text.push(this.styles[tagname][1]); +}; + +/** + * @param {!string} text + */ +_HTMLHandler.prototype.ontext$S = function (text) { + if (this.escape) { + this.text.push(text.replace(/\n/g, "
").replace(/&/g, "&").replace(/"/g, """).replace(//g, ">")); + } else { + this.text.push(text); + } +}; + +/** + * @return {!string} + */ +_HTMLHandler.prototype.result$ = function () { + return this.text.join(''); +}; + +/** + * class SAXParser extends Object + * @constructor + */ +function SAXParser() { +} + +/** + * @constructor + * @param {SAXHandler} handler + */ +function SAXParser$LSAXHandler$(handler) { + this.q = ""; + this.c = ""; + this.bufferCheckPosition = 0; + this.looseCase = ""; + this.tags = [ ]; + this.closed = false; + this.closedRoot = false; + this.sawRoot = false; + this.tag = null; + this.error = null; + this.handler = null; + this.ENTITIES = null; + this.strict = false; + this.tagName = ""; + this.state = 0; + this.line = 0; + this.column = 0; + this.position = 0; + this.startTagPosition = 0; + this.attribName = ""; + this.attribValue = ""; + this.script = ""; + this.textNode = ""; + this.attribList = null; + this.noscript = false; + this.cdata = ""; + this.procInstBody = ""; + this.procInstName = ""; + this.doctype = ""; + this.entity = ""; + this.sgmlDecl = ""; + this.comment = ""; + this.preTags = 0; + this._init$LSAXHandler$B(handler, false); +}; + +SAXParser$LSAXHandler$.prototype = new SAXParser; + +/** + * @constructor + * @param {SAXHandler} handler + * @param {!boolean} strict + */ +function SAXParser$LSAXHandler$B(handler, strict) { + this.q = ""; + this.c = ""; + this.bufferCheckPosition = 0; + this.looseCase = ""; + this.tags = [ ]; + this.closed = false; + this.closedRoot = false; + this.sawRoot = false; + this.tag = null; + this.error = null; + this.handler = null; + this.ENTITIES = null; + this.strict = false; + this.tagName = ""; + this.state = 0; + this.line = 0; + this.column = 0; + this.position = 0; + this.startTagPosition = 0; + this.attribName = ""; + this.attribValue = ""; + this.script = ""; + this.textNode = ""; + this.attribList = null; + this.noscript = false; + this.cdata = ""; + this.procInstBody = ""; + this.procInstName = ""; + this.doctype = ""; + this.entity = ""; + this.sgmlDecl = ""; + this.comment = ""; + this.preTags = 0; + this._init$LSAXHandler$B(handler, strict); +}; + +SAXParser$LSAXHandler$B.prototype = new SAXParser; + +/** + * @param {SAXHandler} handler + * @param {!boolean} strict + */ +SAXParser.prototype._init$LSAXHandler$B = function (handler, strict) { + this.handler = handler; + this.clearBuffers$(); + this.q = ""; + this.bufferCheckPosition = 65536; + this.looseCase = 'toLowerCase'; + this.tags = [ ]; + this.closed = this.closedRoot = this.sawRoot = false; + this.tag = null; + this.error = null; + this.strict = strict; + this.noscript = strict; + this.state = 1; + this.ENTITIES = _Entities$entity_list$(); + this.attribList = [ ]; + this.noscript = false; + this.preTags = 0; +}; + +/** + * @param {!boolean} flag + */ +SAXParser.prototype.set_noscript$B = function (flag) { + this.noscript = flag; +}; + +/** + * @return {SAXParser} + */ +SAXParser.prototype.resume$ = function () { + this.error = null; + return this; +}; + +/** + * @return {SAXParser} + */ +SAXParser.prototype.close$ = function () { + return this.parse$S(''); +}; + +/** + * @param {!string} chunk + * @return {SAXParser} + */ +SAXParser.prototype.parse$S = function (chunk) { + /** @type {Char} */ + var _; + /** @type {!number} */ + var i; + /** @type {!string} */ + var c; + /** @type {!number} */ + var starti; + /** @type {!number} */ + var pad; + /** @type {!number} */ + var returnState; + /** @type {Object.} */ + var charclass$0; + /** @type {Object.} */ + var charclass$1; + /** @type {RegExp} */ + var charclass$2; + /** @type {Object.} */ + var charclass$3; + /** @type {Object.} */ + var charclass$4; + /** @type {Object.} */ + var charclass$5; + /** @type {!string} */ + var text$0; + /** @type {Object.} */ + var charclass$6; + /** @type {RegExp} */ + var charclass$7; + /** @type {Object.} */ + var charclass$8; + /** @type {Object.} */ + var charclass$9; + /** @type {RegExp} */ + var charclass$10; + /** @type {Object.} */ + var charclass$11; + /** @type {RegExp} */ + var charclass$12; + /** @type {Object.} */ + var charclass$13; + /** @type {RegExp} */ + var charclass$14; + /** @type {Object.} */ + var charclass$15; + /** @type {Object.} */ + var charclass$16; + /** @type {Object.} */ + var charclass$17; + /** @type {Object.} */ + var charclass$18; + /** @type {RegExp} */ + var charclass$19; + /** @type {RegExp} */ + var charclass$20; + /** @type {Object.} */ + var charclass$21; + /** @type {Object.} */ + var charclass$22; + /** @type {Object.} */ + var charclass$23; + /** @type {Object.} */ + var charclass$24; + /** @type {!string} */ + var comment$0; + _ = new Char$(); + if (this.error) { + throw this.error; + } + if (this.closed) { + return this.emiterror$S("Cannot write after close. Assign an onready handler."); + } + (i = 0, c = ""); + while (this.c = c = chunk.charAt(i++)) { + this.position++; + if (c === "\n") { + this.handler.line++; + this.handler.column = 0; + } else { + this.handler.column++; + } + switch (this.state) { + case 1: + if (c === "<") { + this.state = 4; + this.startTagPosition = this.position; + } else { + charclass$0 = _.whitespace; + if (! $__jsx_ObjectHasOwnProperty.call(charclass$0, c)) { + this.strictFail$S("Non-whitespace before first tag."); + this.textNode = c; + this.state = 2; + } + } + continue; + case 2: + if (this.sawRoot && ! this.closedRoot) { + starti = i - 1; + while (c && c !== "<" && c !== "&") { + c = chunk.charAt(i++); + if (c) { + this.position++; + if (c === "\n") { + this.handler.line++; + this.handler.column = 0; + } else { + this.handler.column++; + } + } + } + this.textNode += chunk.substring(starti, i - 1); + } + if (c === "<") { + this.state = 4; + this.startTagPosition = this.position; + } else { + if (_.not$HBS(_.whitespace, c) && (! this.sawRoot || this.closedRoot)) { + this.strictFail$S("Text data outside of root node."); + } + if (c === "&") { + this.state = 3; + } else { + this.textNode += c; + } + } + continue; + case 33: + if (c === "<") { + this.state = 34; + } else { + this.script += c; + } + continue; + case 34: + if (c === "/") { + this.state = 31; + } else { + this.script += "<" + c; + this.state = 33; + } + continue; + case 4: + if (c === "!") { + this.state = 5; + this.sgmlDecl = ""; + } else { + charclass$1 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$1, c)) { + } else { + charclass$2 = _.nameStart; + if (charclass$2.test(c)) { + this.state = 21; + this.tagName = c; + } else { + if (c === "/") { + this.state = 31; + this.tagName = ""; + } else { + if (c === "?") { + this.state = 18; + this.procInstName = this.procInstBody = ""; + } else { + this.strictFail$S("Unencoded <"); + if (this.startTagPosition + 1 < this.position) { + pad = this.position - this.startTagPosition; + for (i = 0; i < pad; i++) { + c = " " + c; + } + } + this.textNode += "<" + c; + this.state = 2; + } + } + } + } + } + continue; + case 5: + if ((this.sgmlDecl + c).toUpperCase() === _.CDATA) { + this.closetext_if_exist$(); + this.state = 15; + this.sgmlDecl = ""; + this.cdata = ""; + } else { + if (this.sgmlDecl + c === "--") { + this.state = 12; + this.comment = ""; + this.sgmlDecl = ""; + } else { + if ((this.sgmlDecl + c).toUpperCase() === _.DOCTYPE) { + this.state = 7; + if (this.doctype || this.sawRoot) { + this.strictFail$S("Inappropriately located doctype declaration"); + } + this.doctype = ""; + this.sgmlDecl = ""; + } else { + if (c === ">") { + this.closetext_if_exist$(); + this.sgmlDecl = ""; + this.state = 2; + } else { + charclass$3 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$3, c)) { + this.state = 6; + this.sgmlDecl += c; + } else { + this.sgmlDecl += c; + } + } + } + } + } + continue; + case 6: + if (c === this.q) { + this.state = 5; + this.q = ""; + } + this.sgmlDecl += c; + continue; + case 7: + if (c === ">") { + this.state = 2; + this.closetext_if_exist$(); + this.doctype.trim(); + } else { + this.doctype += c; + if (c === "[") { + this.state = 9; + } else { + charclass$4 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$4, c)) { + this.state = 8; + this.q = c; + } + } + } + continue; + case 8: + this.doctype += c; + if (c === this.q) { + this.q = ""; + this.state = 7; + } + continue; + case 9: + this.doctype += c; + if (c === "]") { + this.state = 7; + } else { + charclass$5 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$5, c)) { + this.state = 10; + this.q = c; + } + } + continue; + case 10: + this.doctype += c; + if (c === this.q) { + this.state = 9; + this.q = ""; + } + continue; + case 12: + if (c === "-") { + this.state = 13; + } else { + this.comment += c; + } + continue; + case 13: + if (c === "-") { + this.state = 14; + text$0 = this.comment; + text$0 = text$0.replace(/[\n\t]/g, ' '); + text$0 = text$0.replace(/\s\s+/g, " "); + comment$0 = this.comment = text$0; + if (comment$0) { + this.closetext_if_exist$(); + this.comment.trim(); + } + this.comment = ""; + } else { + this.comment += "-" + c; + this.state = 12; + } + continue; + case 14: + if (c !== ">") { + this.strictFail$S("Malformed comment"); + this.comment += "--" + c; + this.state = 12; + } else { + this.state = 2; + } + continue; + case 15: + if (c === "]") { + this.state = 16; + } else { + this.cdata += c; + } + continue; + case 16: + if (c === "]") { + this.state = 17; + } else { + this.cdata += "]" + c; + this.state = 15; + } + continue; + case 17: + if (c === ">") { + if (this.cdata) { + this.closetext_if_exist$(); + } + this.cdata = ""; + this.state = 2; + } else { + if (c === "]") { + this.cdata += "]"; + } else { + this.cdata += "]]" + c; + this.state = 15; + } + } + continue; + case 18: + if (c === "?") { + this.state = 20; + } else { + charclass$6 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$6, c)) { + this.state = 19; + } else { + this.procInstName += c; + } + } + continue; + case 19: + if (! this.procInstBody && _.is$HBS(_.whitespace, c)) { + continue; + } else { + if (c === "?") { + this.state = 20; + } else { + this.procInstBody += c; + } + } + continue; + case 20: + if (c === ">") { + this.closetext_if_exist$(); + this.procInstName = this.procInstBody = ""; + this.state = 2; + } else { + this.procInstBody += "?" + c; + this.state = 19; + } + continue; + case 21: + charclass$7 = _.nameBody; + if (charclass$7.test(c)) { + this.tagName += c; + } else { + this.newTag$(); + if (c === ">") { + this.openTag$B(false); + } else { + if (c === "/") { + this.state = 22; + } else { + charclass$8 = _.whitespace; + if (! $__jsx_ObjectHasOwnProperty.call(charclass$8, c)) { + this.strictFail$S("Invalid character in tag name"); + } + this.state = 23; + } + } + } + continue; + case 22: + if (c === ">") { + this.openTag$B(true); + this.closeTag$(); + } else { + this.strictFail$S("Forward-slash in opening tag not followed by >"); + this.state = 23; + } + continue; + case 23: + charclass$9 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$9, c)) { + continue; + } else { + if (c === ">") { + this.openTag$B(false); + } else { + if (c === "/") { + this.state = 22; + } else { + charclass$10 = _.nameStart; + if (charclass$10.test(c)) { + this.attribName = c; + this.attribValue = ""; + this.state = 24; + } else { + this.strictFail$S("Invalid attribute name"); + } + } + } + } + continue; + case 24: + if (c === "=") { + this.state = 26; + } else { + if (c === ">") { + this.strictFail$S("Attribute without value"); + this.attribValue = this.attribName; + this.attrib$(); + this.openTag$B(false); + } else { + charclass$11 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$11, c)) { + this.state = 25; + } else { + charclass$12 = _.nameBody; + if (charclass$12.test(c)) { + this.attribName += c; + } else { + this.strictFail$S("Invalid attribute name"); + } + } + } + } + continue; + case 25: + if (c === "=") { + this.state = 26; + } else { + charclass$13 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$13, c)) { + continue; + } else { + this.strictFail$S("Attribute without value"); + this.tag.attributes[this.attribName] = ""; + this.attribValue = ""; + this.closetext_if_exist$(); + this.attribName = ""; + if (c === ">") { + this.openTag$B(false); + } else { + charclass$14 = _.nameStart; + if (charclass$14.test(c)) { + this.attribName = c; + this.state = 24; + } else { + this.strictFail$S("Invalid attribute name"); + this.state = 23; + } + } + } + } + continue; + case 26: + charclass$15 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$15, c)) { + continue; + } else { + charclass$16 = _.quote; + if ($__jsx_ObjectHasOwnProperty.call(charclass$16, c)) { + this.q = c; + this.state = 27; + } else { + this.strictFail$S("Unquoted attribute value"); + this.state = 28; + this.attribValue = c; + } + } + continue; + case 27: + if (c !== this.q) { + if (c === "&") { + this.state = 29; + } else { + this.attribValue += c; + } + continue; + } + this.attrib$(); + this.q = ""; + this.state = 23; + continue; + case 28: + charclass$17 = _.attribEnd; + if (! $__jsx_ObjectHasOwnProperty.call(charclass$17, c)) { + if (c === "&") { + this.state = 30; + } else { + this.attribValue += c; + } + continue; + } + this.attrib$(); + if (c === ">") { + this.openTag$B(false); + } else { + this.state = 23; + } + continue; + case 31: + if (! this.tagName) { + charclass$18 = _.whitespace; + if ($__jsx_ObjectHasOwnProperty.call(charclass$18, c)) { + continue; + } else { + charclass$19 = _.nameStart; + if (! charclass$19.test(c)) { + if (this.script) { + this.script += "") { + this.closeTag$(); + } else { + charclass$20 = _.nameBody; + if (charclass$20.test(c)) { + this.tagName += c; + } else { + if (this.script) { + this.script += "") { + this.closeTag$(); + } else { + this.strictFail$S("Invalid characters in closing tag"); + } + continue; + case 3: + if (c === ";") { + this.textNode += this.parseEntity$(); + this.entity = ""; + this.state = 2; + } else { + charclass$23 = _.entity; + if ($__jsx_ObjectHasOwnProperty.call(charclass$23, c)) { + this.entity += c; + } else { + this.strictFail$S("Invalid character entity"); + this.textNode += "&" + this.entity + c; + this.entity = ""; + this.state = 2; + } + } + continue; + case 29: + case 30: + if (this.state === 29) { + returnState = 27; + } else { + returnState = 28; + } + if (c === ";") { + this.attribValue += this.parseEntity$(); + this.entity = ""; + this.state = (returnState | 0); + } else { + charclass$24 = _.entity; + if ($__jsx_ObjectHasOwnProperty.call(charclass$24, c)) { + this.entity += c; + } else { + this.strictFail$S("Invalid character entity"); + this.attribValue += "&" + this.entity + c; + this.entity = ""; + this.state = (returnState | 0); + } + } + continue; + default: + throw new Error("Unknown state: " + (this.state + "")); + } + } + this.end$(); + return this; +}; + +/** + */ +SAXParser.prototype.clearBuffers$ = function () { + this.comment = ''; + this.sgmlDecl = ''; + this.textNode = ''; + this.tagName = ''; + this.doctype = ''; + this.procInstName = ''; + this.procInstBody = ''; + this.entity = ''; + this.attribName = ''; + this.attribValue = ''; + this.cdata = ''; + this.script = ''; +}; + +/** + */ +SAXParser.prototype.closetext_if_exist$ = function () { + if (this.textNode !== '') { + this.closetext$(); + } +}; + +/** + */ +SAXParser.prototype.closetext$ = function () { + /** @type {!string} */ + var text; + /** @type {!string} */ + var text$0; + if (this.preTags === 0) { + text$0 = this.textNode; + text$0 = text$0.replace(/[\n\t]/g, ' '); + text$0 = text$0.replace(/\s\s+/g, " "); + text = text$0; + if (text$0) { + this.handler.ontext$S(text); + } + } else { + if (this.textNode) { + this.handler.ontext$S(this.textNode); + } + } + this.textNode = ""; +}; + +/** + * @param {!string} text + * @return {!string} + */ +SAXParser.prototype.textopts$S = function (text) { + text = text.replace(/[\n\t]/g, ' '); + text = text.replace(/\s\s+/g, " "); + return text; +}; + +/** + * @param {!string} er + * @return {SAXParser} + */ +SAXParser.prototype.emiterror$S = function (er) { + /** @type {Error} */ + var error; + this.closetext$(); + er += "\nLine: " + (this.line + "") + "\nColumn: " + (this.column + "") + "\nChar: " + this.c; + error = new Error(er); + this.error = error; + return this; +}; + +/** + */ +SAXParser.prototype.end$ = function () { + if (! this.closedRoot) { + this.strictFail$S("Unclosed root tag"); + } + if (this.state !== 2) { + this.emiterror$S("Unexpected end"); + } + this.closetext$(); + this.c = ""; + this.closed = true; +}; + +/** + * @param {!string} message + */ +SAXParser.prototype.strictFail$S = function (message) { + if (this.strict) { + this.emiterror$S(message); + } +}; + +/** + */ +SAXParser.prototype.newTag$ = function () { + if (! this.strict) { + this.tagName = this.tagName.toLowerCase(); + } + this.tag = ({name: this.tagName, attributes: ({ }), isSelfClosing: false}); + this.attribList.length = 0; +}; + +/** + */ +SAXParser.prototype.attrib$ = function () { + if (! this.strict) { + this.attribName = this.attribName.toLowerCase(); + } + if ($__jsx_ObjectHasOwnProperty.call(this.tag.attributes, this.attribName)) { + this.attribName = this.attribValue = ""; + return; + } + this.tag.attributes[this.attribName] = this.attribValue; + this.closetext_if_exist$(); + this.attribName = this.attribValue = ""; +}; + +/** + */ +SAXParser.prototype.openTag$ = function () { + this.openTag$B(false); +}; + +/** + * @param {!boolean} selfClosing + */ +SAXParser.prototype.openTag$B = function (selfClosing) { + /** @type {Tag} */ + var tag$0; + /** @type {Tag} */ + var tag$1; + (tag$0 = this.tag).isSelfClosing = selfClosing; + this.sawRoot = true; + this.tags.push(tag$0); + this.closetext_if_exist$(); + this.handler.onopentag$SHS((tag$1 = this.tag).name, tag$1.attributes); + if (this.tag.name === 'pre') { + this.preTags++; + } + if (! selfClosing) { + if (! this.noscript && this.tagName.toLowerCase() === "script") { + this.state = 33; + } else { + this.state = 2; + } + this.tag = null; + this.tagName = ""; + } + this.attribName = this.attribValue = ""; + this.attribList.length = 0; +}; + +/** + */ +SAXParser.prototype.closeTag$ = function () { + /** @type {!number} */ + var t; + /** @type {!string} */ + var tagName; + /** @type {!string} */ + var closeTo; + /** @type {Tag} */ + var close; + /** @type {!number} */ + var s; + /** @type {Tag} */ + var tag$0; + if (! this.tagName) { + this.strictFail$S("Weird empty close tag."); + this.textNode += ""; + this.state = 2; + return; + } + if (this.script) { + if (this.tagName !== "script") { + this.script += ""; + this.tagName = ""; + this.state = 33; + return; + } + this.closetext_if_exist$(); + this.script = ""; + } + t = this.tags.length; + tagName = this.tagName; + if (! this.strict) { + tagName = tagName.toLowerCase(); + } + closeTo = tagName; + while (t--) { + close = this.tags[t]; + if (close.name !== closeTo) { + this.strictFail$S("Unexpected close tag"); + } else { + break; + } + } + if (t < 0) { + this.strictFail$S("Unmatched closing tag: " + this.tagName); + this.textNode += ""; + this.state = 2; + return; + } + this.tagName = tagName; + s = this.tags.length; + while (s-- > t) { + tag$0 = this.tag = this.tags.pop(); + this.tagName = tag$0.name; + this.closetext_if_exist$(); + this.handler.onclosetag$S(this.tagName); + if (this.tagName === 'pre') { + this.preTags--; + } + } + if (t === 0) { + this.closedRoot = true; + } + this.tagName = this.attribValue = this.attribName = ""; + this.attribList.length = 0; + this.state = 2; +}; + +/** + * @return {!string} + */ +SAXParser.prototype.parseEntity$ = function () { + /** @type {!string} */ + var entity; + /** @type {!string} */ + var entityLC; + /** @type {!number} */ + var num; + /** @type {!string} */ + var numStr; + entity = this.entity; + entityLC = entity.toLowerCase(); + num = 0; + numStr = ""; + if (this.ENTITIES[entity]) { + return this.ENTITIES[entity]; + } + if (this.ENTITIES[entityLC]) { + return this.ENTITIES[entityLC]; + } + entity = entityLC; + if (entityLC.charAt(0) === "#") { + if (entity.charAt(1) === "x") { + entity = entity.slice(2); + num = $__jsx_parseInt(entity, 16); + numStr = num.toString(16); + } else { + entity = entity.slice(1); + num = $__jsx_parseInt(entity, 10); + numStr = num.toString(10); + } + } + entity = entity.replace(/^0+/, ""); + if (numStr.toLowerCase() !== entity) { + this.strictFail$S("Invalid character entity"); + return "&" + this.entity + ";"; + } + return String.fromCharCode(num); +}; + +/** + * class Char extends Object + * @constructor + */ +function Char() { +} + +/** + * @constructor + */ +function Char$() { + this.CDATA = "[CDATA["; + this.DOCTYPE = "DOCTYPE"; + this.XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace"; + this.whitespace = this._charClass$S("\r\n\t "); + this.number = this._charClass$S("0124356789"); + this.letter = this._charClass$S("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + this.quote = this._charClass$S("'\""); + this.entity = this._charClass$S("0124356789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#"); + this.attribEnd = this._charClass$S("\r\n\t >"); + this.nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/; + this.nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040\.\d-]/; +}; + +Char$.prototype = new Char; + +/** + * @param {!string} str + * @return {Object.} + */ +Char.prototype._charClass$S = function (str) { + /** @type {Object.} */ + var result; + /** @type {!number} */ + var i; + result = ({ }); + for (i = 0; i < str.length; i++) { + result[str.slice(i, i + 1)] = true; + } + return result; +}; + +/** + * @param {RegExp} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.is$LRegExp$S = function (charclass, c) { + return charclass.test(c); +}; + +/** + * @param {Object.} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.is$HBS = function (charclass, c) { + return $__jsx_ObjectHasOwnProperty.call(charclass, c); +}; + +/** + * @param {RegExp} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.not$LRegExp$S = function (charclass, c) { + return ! charclass.test(c); +}; + +/** + * @param {Object.} charclass + * @param {!string} c + * @return {!boolean} + */ +Char.prototype.not$HBS = function (charclass, c) { + return ! $__jsx_ObjectHasOwnProperty.call(charclass, c); +}; + +/** + * class _Entities extends Object + * @constructor + */ +function _Entities() { +} + +/** + * @constructor + */ +function _Entities$() { +}; + +_Entities$.prototype = new _Entities; + +/** + * @return {Object.} + */ +_Entities.entity_list$ = function () { + /** @type {Object.} */ + var result; + /** @type {!string} */ + var key; + /** @type {*} */ + var value; + result = ({ }); + for (key in _Entities._entities) { + value = _Entities._entities[key]; + if (typeof value === 'string') { + result[key] = value + ""; + } else { + if (typeof value === 'number') { + result[key] = String.fromCharCode(value | 0); + } + } + } + return result; +}; + +var _Entities$entity_list$ = _Entities.entity_list$; + +/** + * class BitVector extends Object + * @constructor + */ +function BitVector() { +} + +/** + * @constructor + */ +function BitVector$() { + /** @type {Array.} */ + var _v$0; + /** @type {Array.} */ + var _r$0; + _r$0 = this._r = [ ]; + _v$0 = this._v = [ ]; + _v$0.length = 0; + _r$0.length = 0; + this._size = 0; + this._size1 = 0; +}; + +BitVector$.prototype = new BitVector; + +/** + */ +BitVector.prototype.build$ = function () { + /** @type {!number} */ + var i; + this._size1 = 0; + for (i = 0; i < this._v.length; i++) { + if (i % 8 === 0) { + this._r.push(true ? this._size1 : this._size - this._size1); + } + this._size1 += this._rank32$IIB(this._v[i], 32, true); + } +}; + +/** + */ +BitVector.prototype.clear$ = function () { + this._v.length = 0; + this._r.length = 0; + this._size = 0; + this._size1 = 0; +}; + +/** + * @return {!number} + */ +BitVector.prototype.size$ = function () { + return this._size; +}; + +/** + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype.size$B = function (b) { + return (b ? this._size1 : this._size - this._size1); +}; + +/** + * @param {!number} value + */ +BitVector.prototype.set$I = function (value) { + this.set$IB(value, true); +}; + +/** + * @param {!number} value + * @param {!boolean} flag + */ +BitVector.prototype.set$IB = function (value, flag) { + /** @type {!number} */ + var q; + /** @type {!number} */ + var r; + /** @type {!number} */ + var m; + if (value >= this._size) { + this._size = (value + 1 | 0); + } + q = (value / 32 | 0); + r = (value % 32 | 0); + while (q >= this._v.length) { + this._v.push(0); + } + m = 0x1 << r; + if (flag) { + this._v[q] |= m; + } else { + this._v[q] &= ~ m; + } +}; + +/** + * @param {!number} value + * @return {!boolean} + */ +BitVector.prototype.get$I = function (value) { + /** @type {!number} */ + var q; + /** @type {!number} */ + var r; + /** @type {!number} */ + var m; + if (value >= this._size) { + throw new Error("BitVector.get() : range error"); + } + q = (value / 32 | 0); + r = (value % 32 | 0); + m = 0x1 << r; + return !! (this._v[q] & m); +}; + +/** + * @param {!number} i + * @return {!number} + */ +BitVector.prototype.rank$I = function (i) { + return this.rank$IB(i, true); +}; + +/** + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype.rank$IB = function (i, b) { + /** @type {!number} */ + var q_large; + /** @type {!number} */ + var q_small; + /** @type {!number} */ + var r; + /** @type {!number} */ + var rank; + /** @type {!number} */ + var begin; + /** @type {!number} */ + var j; + if (i > this._size) { + throw new Error("BitVector.rank() : range error"); + } + if (i === 0) { + return 0; + } + i--; + q_large = (Math.floor(i / 256) | 0); + q_small = (Math.floor(i / 32) | 0); + r = (Math.floor(i % 32) | 0); + rank = (this._r[q_large] | 0); + if (! b) { + rank = q_large * 256 - rank; + } + begin = q_large * 8; + for (j = begin; j < q_small; j++) { + rank += this._rank32$IIB(this._v[j], 32, b); + } + rank += this._rank32$IIB(this._v[q_small], r + 1, b); + return rank; +}; + +/** + * @param {!number} i + * @return {!number} + */ +BitVector.prototype.select$I = function (i) { + return this.select$IB(i, true); +}; + +/** + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype.select$IB = function (i, b) { + /** @type {!number} */ + var left; + /** @type {!number} */ + var right; + /** @type {!number} */ + var pivot; + /** @type {undefined|!number} */ + var rank; + /** @type {!number} */ + var j; + if (i >= (b ? this._size1 : this._size - this._size1)) { + throw new Error("BitVector.select() : range error"); + } + left = 0; + right = this._r.length; + while (left < right) { + pivot = Math.floor((left + right) / 2); + rank = this._r[pivot]; + if (! b) { + rank = pivot * 256 - rank; + } + if (i < rank) { + right = pivot; + } else { + left = pivot + 1; + } + } + right--; + if (b) { + i -= (this._r[right] | 0); + } else { + i -= (right * 256 - this._r[right] | 0); + } + j = right * 8; + while (1) { + rank = this._rank32$IIB(this._v[j], 32, b); + if (i < rank) { + break; + } + j++; + i -= (rank | 0); + } + return (j * 32 + this._select32$IIB(this._v[j], i, b) | 0); +}; + +/** + * @param {!number} x + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype._rank32$IIB = function (x, i, b) { + if (! b) { + x = ~ x; + } + x <<= 32 - i; + x = ((x & 0xaaaaaaaa) >>> 1) + (x & 0x55555555); + x = ((x & 0xcccccccc) >>> 2) + (x & 0x33333333); + x = ((x & 0xf0f0f0f0) >>> 4) + (x & 0x0f0f0f0f); + x = ((x & 0xff00ff00) >>> 8) + (x & 0x00ff00ff); + x = ((x & 0xffff0000) >>> 16) + (x & 0x0000ffff); + return x; +}; + +/** + * @param {!number} x + * @param {!number} i + * @param {!boolean} b + * @return {!number} + */ +BitVector.prototype._select32$IIB = function (x, i, b) { + /** @type {!number} */ + var x1; + /** @type {!number} */ + var x2; + /** @type {!number} */ + var x3; + /** @type {!number} */ + var x4; + /** @type {!number} */ + var x5; + /** @type {!number} */ + var pos; + /** @type {!number} */ + var v5; + /** @type {!number} */ + var v4; + /** @type {!number} */ + var v3; + /** @type {!number} */ + var v2; + /** @type {!number} */ + var v1; + /** @type {!number} */ + var v0; + if (! b) { + x = ~ x; + } + x1 = ((x & 0xaaaaaaaa) >>> 1) + (x & 0x55555555); + x2 = ((x1 & 0xcccccccc) >>> 2) + (x1 & 0x33333333); + x3 = ((x2 & 0xf0f0f0f0) >>> 4) + (x2 & 0x0f0f0f0f); + x4 = ((x3 & 0xff00ff00) >>> 8) + (x3 & 0x00ff00ff); + x5 = ((x4 & 0xffff0000) >>> 16) + (x4 & 0x0000ffff); + i++; + pos = 0; + v5 = x5 & 0xffffffff; + if (i > v5) { + i -= (v5 | 0); + pos += 32; + } + v4 = x4 >>> pos & 0x0000ffff; + if (i > v4) { + i -= (v4 | 0); + pos += 16; + } + v3 = x3 >>> pos & 0x000000ff; + if (i > v3) { + i -= (v3 | 0); + pos += 8; + } + v2 = x2 >>> pos & 0x0000000f; + if (i > v2) { + i -= (v2 | 0); + pos += 4; + } + v1 = x1 >>> pos & 0x00000003; + if (i > v1) { + i -= (v1 | 0); + pos += 2; + } + v0 = x >>> pos & 0x00000001; + if (i > v0) { + i -= (v0 | 0); + pos += 1; + } + return (pos | 0); +}; + +/** + * @return {!string} + */ +BitVector.prototype.dump$ = function () { + /** @type {Array.} */ + var contents; + contents = [ ]; + contents.push(Binary$dump32bitNumber$N(this._size)); + contents.push(Binary$dump32bitNumberList$AN(this._v)); + return contents.join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +BitVector.prototype.dump$LCompressionReport$ = function (report) { + /** @type {Array.} */ + var contents; + contents = [ ]; + contents.push(Binary$dump32bitNumber$N(this._size)); + CompressionReport$add$LCompressionReport$II(report, 2, 2); + contents.push(Binary$dump32bitNumberList$ANLCompressionReport$(this._v, report)); + return contents.join(''); +}; + +/** + * @param {!string} data + * @return {!number} + */ +BitVector.prototype.load$S = function (data) { + return this.load$SI(data, 0); +}; + +/** + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +BitVector.prototype.load$SI = function (data, offset) { + /** @type {LoadedNumberListResult} */ + var result; + /** @type {!number} */ + var result$0; + this._v.length = 0; + this._r.length = 0; + this._size = 0; + this._size1 = 0; + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + this._size = (result$0 | 0); + result = Binary$load32bitNumberList$SI(data, offset + 2); + this._v = result.result; + this.build$(); + return result.offset; +}; + +/** + * class WaveletMatrix extends Object + * @constructor + */ +function WaveletMatrix() { +} + +/** + * @constructor + */ +function WaveletMatrix$() { + /** @type {Array.} */ + var _bv$0; + /** @type {Array.} */ + var _seps$0; + this._range = ({ }); + _bv$0 = this._bv = [ ]; + _seps$0 = this._seps = [ ]; + this._bitsize = 16; + _bv$0.length = 0; + _seps$0.length = 0; + this._size = 0; +}; + +WaveletMatrix$.prototype = new WaveletMatrix; + +/** + * @return {!number} + */ +WaveletMatrix.prototype.bitsize$ = function () { + return this._bitsize; +}; + +/** + * @param {!number} charCode + */ +WaveletMatrix.prototype.setMaxCharCode$I = function (charCode) { + this._bitsize = (Math.ceil(Math.log(charCode) / 0.6931471805599453) | 0); +}; + +/** + */ +WaveletMatrix.prototype.clear$ = function () { + this._bv.length = 0; + this._seps.length = 0; + this._size = 0; +}; + +/** + * @param {!string} v + */ +WaveletMatrix.prototype.build$S = function (v) { + /** @type {!number} */ + var size; + /** @type {!number} */ + var bitsize; + /** @type {!number} */ + var i; + /** @type {!number} */ + var depth; + /** @type {Object.} */ + var range_tmp; + /** @type {!number} */ + var code; + /** @type {!boolean} */ + var bit; + /** @type {!number} */ + var key; + /** @type {Object.} */ + var range_rev; + /** @type {!string} */ + var range_key; + /** @type {!number} */ + var value; + /** @type {!number} */ + var pos0; + /** @type {undefined|!number} */ + var pos1; + /** @type {!string} */ + var range_rev_key; + /** @type {!number} */ + var begin; + /** @type {undefined|!number} */ + var end; + /** @type {!number} */ + var num0; + /** @type {!number} */ + var num1; + this._bv.length = 0; + this._seps.length = 0; + this._size = 0; + size = v.length; + bitsize = this._bitsize; + for (i = 0; i < bitsize; i++) { + this._bv.push(new BitVector$()); + this._seps.push(0); + } + this._size = (size | 0); + for (i = 0; i < size; i++) { + this._bv[0].set$IB(i, this._uint2bit$II(v.charCodeAt(i), 0)); + } + this._bv[0].build$(); + this._seps[0] = this._bv[0].size$B(false); + this._range["0"] = 0; + this._range["1"] = this._seps[0]; + depth = 1; + while (depth < bitsize) { + range_tmp = WaveletMatrix$_shallow_copy$HI(this._range); + for (i = 0; i < size; i++) { + code = v.charCodeAt(i); + bit = this._uint2bit$II(code, depth); + key = code >>> bitsize - depth; + this._bv[depth].set$IB(range_tmp[key + ""], bit); + range_tmp[key + ""]++; + } + this._bv[depth].build$(); + this._seps[depth] = this._bv[depth].size$B(false); + range_rev = ({ }); + for (range_key in this._range) { + value = this._range[range_key]; + if (value != range_tmp[range_key]) { + range_rev[value + ""] = range_key | 0; + } + } + this._range = ({ }); + pos0 = 0; + pos1 = this._seps[depth]; + for (range_rev_key in range_rev) { + begin = range_rev_key | 0; + value = range_rev[range_rev_key]; + end = range_tmp[value + ""]; + num0 = this._bv[depth].rank$IB(end, false) - this._bv[depth].rank$IB(begin, false); + num1 = end - begin - num0; + if (num0 > 0) { + this._range[(value << 1) + ""] = (pos0 | 0); + pos0 += num0; + } + if (num1 > 0) { + this._range[(value << 1) + 1 + ""] = pos1; + pos1 += (num1 | 0); + } + } + depth++; + } +}; + +/** + * @return {!number} + */ +WaveletMatrix.prototype.size$ = function () { + return this._size; +}; + +/** + * @param {!number} c + * @return {!number} + */ +WaveletMatrix.prototype.size$I = function (c) { + return this.rank$II(this._size, c); +}; + +/** + * @param {!number} i + * @return {!number} + */ +WaveletMatrix.prototype.get$I = function (i) { + /** @type {!number} */ + var value; + /** @type {!number} */ + var depth; + /** @type {!boolean} */ + var bit; + if (i >= this._size) { + throw new Error("WaveletMatrix.get() : range error"); + } + value = 0; + depth = 0; + while (depth < this._bitsize) { + bit = this._bv[depth].get$I(i); + i = this._bv[depth].rank$IB(i, bit); + value <<= 1; + if (bit) { + i += this._seps[depth]; + value += 1; + } + depth++; + } + return (value | 0); +}; + +/** + * @param {!number} i + * @param {!number} c + * @return {!number} + */ +WaveletMatrix.prototype.rank$II = function (i, c) { + /** @type {undefined|!number} */ + var begin; + /** @type {!number} */ + var end; + /** @type {!number} */ + var depth; + /** @type {!boolean} */ + var bit; + if (i > this._size) { + throw new Error("WaveletMatrix.rank(): range error"); + } + if (i === 0) { + return 0; + } + begin = this._range[c + ""]; + if (begin == null) { + return 0; + } + end = i; + depth = 0; + while (depth < this._bitsize) { + bit = this._uint2bit$II(c, depth); + end = this._bv[depth].rank$IB(end, bit); + if (bit) { + end += this._seps[depth]; + } + depth++; + } + return (end - begin | 0); +}; + +/** + * @param {!number} i + * @param {!number} c + * @return {!number} + */ +WaveletMatrix.prototype.rank_less_than$II = function (i, c) { + /** @type {!number} */ + var begin; + /** @type {!number} */ + var end; + /** @type {!number} */ + var depth; + /** @type {!number} */ + var rlt; + /** @type {!number} */ + var rank0_begin; + /** @type {!number} */ + var rank0_end; + /** @type {Array.} */ + var _seps$0; + if (i > this._size) { + throw new Error("WaveletMatrix.rank_less_than(): range error"); + } + if (i === 0) { + return 0; + } + begin = 0; + end = i; + depth = 0; + rlt = 0; + while (depth < this._bitsize) { + rank0_begin = this._bv[depth].rank$IB(begin, false); + rank0_end = this._bv[depth].rank$IB(end, false); + if (this._uint2bit$II(c, depth)) { + rlt += rank0_end - rank0_begin; + begin += (_seps$0 = this._seps)[depth] - rank0_begin; + end += _seps$0[depth] - rank0_end; + } else { + begin = rank0_begin; + end = rank0_end; + } + depth++; + } + return (rlt | 0); +}; + +/** + * @return {!string} + */ +WaveletMatrix.prototype.dump$ = function () { + /** @type {Array.} */ + var contents; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var range_contents; + /** @type {!number} */ + var counter; + /** @type {!string} */ + var key; + contents = [ Binary$dump16bitNumber$I(this._bitsize), Binary$dump32bitNumber$N(this._size) ]; + for (i = 0; i < this._bitsize; i++) { + contents.push(this._bv[i].dump$()); + } + for (i = 0; i < this._bitsize; i++) { + contents.push(Binary$dump32bitNumber$N(this._seps[i])); + } + range_contents = [ ]; + counter = 0; + for (key in this._range) { + range_contents.push(Binary$dump32bitNumber$N(key | 0)); + range_contents.push(Binary$dump32bitNumber$N(this._range[key])); + counter++; + } + contents.push(Binary$dump32bitNumber$N(counter)); + return contents.join('') + range_contents.join(''); +}; + +/** + * @param {CompressionReport} report + * @return {!string} + */ +WaveletMatrix.prototype.dump$LCompressionReport$ = function (report) { + /** @type {Array.} */ + var contents; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var range_contents; + /** @type {!number} */ + var counter; + /** @type {!string} */ + var key; + contents = [ Binary$dump16bitNumber$I(this._bitsize), Binary$dump32bitNumber$N(this._size) ]; + CompressionReport$add$LCompressionReport$II(report, 3, 3); + for (i = 0; i < this._bitsize; i++) { + contents.push(this._bv[i].dump$LCompressionReport$(report)); + } + for (i = 0; i < this._bitsize; i++) { + contents.push(Binary$dump32bitNumber$N(this._seps[i])); + CompressionReport$add$LCompressionReport$II(report, 2, 2); + } + range_contents = [ ]; + counter = 0; + for (key in this._range) { + range_contents.push(Binary$dump32bitNumber$N(key | 0)); + range_contents.push(Binary$dump32bitNumber$N(this._range[key])); + CompressionReport$add$LCompressionReport$II(report, 4, 4); + counter++; + } + CompressionReport$add$LCompressionReport$II(report, 2, 2); + contents.push(Binary$dump32bitNumber$N(counter)); + return contents.join('') + range_contents.join(''); +}; + +/** + * @param {!string} data + * @return {!number} + */ +WaveletMatrix.prototype.load$S = function (data) { + return this.load$SI(data, 0); +}; + +/** + * @param {!string} data + * @param {!number} offset + * @return {!number} + */ +WaveletMatrix.prototype.load$SI = function (data, offset) { + /** @type {!number} */ + var i; + /** @type {BitVector} */ + var bit_vector; + /** @type {!number} */ + var range_size; + /** @type {!number} */ + var value; + /** @type {!number} */ + var offset$0; + /** @type {!number} */ + var result$0; + /** @type {!number} */ + var result$1; + /** @type {!number} */ + var result$2; + this._bv.length = 0; + this._seps.length = 0; + this._size = 0; + offset$0 = offset++; + this._bitsize = (data.charCodeAt(offset$0) | 0); + result$0 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + this._size = (result$0 | 0); + offset += 2; + for (i = 0; i < this._bitsize; i++) { + bit_vector = new BitVector$(); + offset = bit_vector.load$SI(data, offset); + this._bv.push(bit_vector); + } + for (i = 0; i < this._bitsize; (i++, offset += 2)) { + this._seps.push(Binary$load32bitNumber$SI(data, offset)); + } + result$1 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + range_size = result$1; + offset += 2; + for (i = 0; i < range_size; (i++, offset += 4)) { + result$2 = data.charCodeAt(offset) * 65536 + data.charCodeAt(offset + 1); + value = Binary$load32bitNumber$SI(data, offset + 2); + this._range[result$2 + ""] = (value | 0); + } + return offset; +}; + +/** + * @param {Object.} input + * @return {Object.} + */ +WaveletMatrix._shallow_copy$HI = function (input) { + /** @type {Object.} */ + var result; + /** @type {!string} */ + var key; + result = ({ }); + for (key in input) { + result[key] = input[key]; + } + return result; +}; + +var WaveletMatrix$_shallow_copy$HI = WaveletMatrix._shallow_copy$HI; + +/** + * @param {!number} c + * @param {!number} i + * @return {!boolean} + */ +WaveletMatrix.prototype._uint2bit$II = function (c, i) { + return (c >>> this._bitsize - 1 - i & 0x1) === 0x1; +}; + +/** + * class BurrowsWheelerTransform extends Object + * @constructor + */ +function BurrowsWheelerTransform() { +} + +/** + * @constructor + */ +function BurrowsWheelerTransform$() { + this._str = ""; + this._size = 0; + this._head = 0; + this._suffixarray = [ ]; +}; + +BurrowsWheelerTransform$.prototype = new BurrowsWheelerTransform; + +/** + * @param {BurrowsWheelerTransform} $this + * @return {!number} + */ +BurrowsWheelerTransform.size$LBurrowsWheelerTransform$ = function ($this) { + return $this._size; +}; + +var BurrowsWheelerTransform$size$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.size$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + * @return {!number} + */ +BurrowsWheelerTransform.head$LBurrowsWheelerTransform$ = function ($this) { + return $this._head; +}; + +var BurrowsWheelerTransform$head$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.head$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + */ +BurrowsWheelerTransform.clear$LBurrowsWheelerTransform$ = function ($this) { + $this._str = ""; + $this._size = 0; + $this._head = 0; + $this._suffixarray.length = 0; +}; + +var BurrowsWheelerTransform$clear$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.clear$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + * @param {!string} str + */ +BurrowsWheelerTransform.build$LBurrowsWheelerTransform$S = function ($this, str) { + /** @type {!string} */ + var _str$0; + /** @type {Array.} */ + var _suffixarray$0; + _str$0 = $this._str = str; + $this._size = _str$0.length; + _suffixarray$0 = $this._suffixarray = SAIS$make$S(str); + $this._head = (_suffixarray$0.indexOf(0) | 0); +}; + +var BurrowsWheelerTransform$build$LBurrowsWheelerTransform$S = BurrowsWheelerTransform.build$LBurrowsWheelerTransform$S; + +/** + * @param {BurrowsWheelerTransform} $this + * @param {!number} i + * @return {!string} + */ +BurrowsWheelerTransform.get$LBurrowsWheelerTransform$I = function ($this, i) { + /** @type {!number} */ + var size; + /** @type {!number} */ + var index; + size = $this._size; + if (i >= size) { + throw new Error("BurrowsWheelerTransform.get() : range error"); + } + index = ($this._suffixarray[i] + size - 1) % size; + return $this._str.charAt(index); +}; + +var BurrowsWheelerTransform$get$LBurrowsWheelerTransform$I = BurrowsWheelerTransform.get$LBurrowsWheelerTransform$I; + +/** + * @param {BurrowsWheelerTransform} $this + * @return {!string} + */ +BurrowsWheelerTransform.get$LBurrowsWheelerTransform$ = function ($this) { + /** @type {Array.} */ + var str; + /** @type {!number} */ + var size; + /** @type {!number} */ + var i; + str = [ ]; + size = $this._size; + for (i = 0; i < size; i++) { + str.push(BurrowsWheelerTransform$get$LBurrowsWheelerTransform$I($this, i)); + } + return str.join(""); +}; + +var BurrowsWheelerTransform$get$LBurrowsWheelerTransform$ = BurrowsWheelerTransform.get$LBurrowsWheelerTransform$; + +/** + * @param {BurrowsWheelerTransform} $this + * @param {!string} replace + * @return {!string} + */ +BurrowsWheelerTransform.get$LBurrowsWheelerTransform$S = function ($this, replace) { + /** @type {!string} */ + var result; + result = BurrowsWheelerTransform$get$LBurrowsWheelerTransform$($this); + return result.replace(BurrowsWheelerTransform.END_MARKER, replace); +}; + +var BurrowsWheelerTransform$get$LBurrowsWheelerTransform$S = BurrowsWheelerTransform.get$LBurrowsWheelerTransform$S; + +/** + * class OArray extends Object + * @constructor + */ +function OArray() { +} + +/** + * @constructor + * @param {Array.} array + */ +function OArray$AI(array) { + this.array = array; + this.offset = 0; +}; + +OArray$AI.prototype = new OArray; + +/** + * @constructor + * @param {Array.} array + * @param {!number} offset + */ +function OArray$AII(array, offset) { + this.array = array; + this.offset = offset; +}; + +OArray$AII.prototype = new OArray; + +/** + * @param {OArray} $this + * @param {!number} index + * @return {!number} + */ +OArray.get$LOArray$I = function ($this, index) { + return $this.array[index + $this.offset]; +}; + +var OArray$get$LOArray$I = OArray.get$LOArray$I; + +/** + * @param {OArray} $this + * @param {!number} index + * @param {!number} value + */ +OArray.set$LOArray$II = function ($this, index, value) { + $this.array[index + $this.offset] = value; +}; + +var OArray$set$LOArray$II = OArray.set$LOArray$II; + +/** + * @param {OArray} $this + * @param {!number} index + * @return {!boolean} + */ +OArray.isS$LOArray$I = function ($this, index) { + /** @type {Array.} */ + var array$0; + /** @type {!number} */ + var offset$0; + return (array$0 = $this.array)[index + (offset$0 = $this.offset)] < array$0[index + offset$0 + 1]; +}; + +var OArray$isS$LOArray$I = OArray.isS$LOArray$I; + +/** + * @param {OArray} $this + * @param {!number} index1 + * @param {!number} index2 + * @return {!boolean} + */ +OArray.compare$LOArray$II = function ($this, index1, index2) { + /** @type {Array.} */ + var array$0; + /** @type {!number} */ + var offset$0; + return (array$0 = $this.array)[index1 + (offset$0 = $this.offset)] == array$0[index2 + offset$0]; +}; + +var OArray$compare$LOArray$II = OArray.compare$LOArray$II; + +/** + * class SAIS extends Object + * @constructor + */ +function SAIS() { +} + +/** + * @constructor + */ +function SAIS$() { +}; + +SAIS$.prototype = new SAIS; + +/** + * @param {BitVector} t + * @param {!number} i + * @return {!boolean} + */ +SAIS._isLMS$LBitVector$I = function (t, i) { + return i > 0 && t.get$I(i) && ! t.get$I(i - 1); +}; + +var SAIS$_isLMS$LBitVector$I = SAIS._isLMS$LBitVector$I; + +/** + * @param {OArray} s + * @param {Array.} bkt + * @param {!number} n + * @param {!number} K + * @param {!boolean} end + */ +SAIS._getBuckets$LOArray$AIIIB = function (s, bkt, n, K, end) { + /** @type {!number} */ + var sum; + /** @type {!number} */ + var i; + sum = 0; + for (i = 0; i <= K; i++) { + bkt[i] = 0; + } + for (i = 0; i < n; i++) { + bkt[OArray$get$LOArray$I(s, i)]++; + } + for (i = 0; i <= K; i++) { + sum += bkt[i]; + bkt[i] = ((end ? sum : sum - bkt[i]) | 0); + } +}; + +var SAIS$_getBuckets$LOArray$AIIIB = SAIS._getBuckets$LOArray$AIIIB; + +/** + * @param {BitVector} t + * @param {Array.} SA + * @param {OArray} s + * @param {Array.} bkt + * @param {!number} n + * @param {!number} K + * @param {!boolean} end + */ +SAIS._induceSAl$LBitVector$AILOArray$AIIIB = function (t, SA, s, bkt, n, K, end) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var j; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, end); + for (i = 0; i < n; i++) { + j = SA[i] - 1; + if (j >= 0 && ! t.get$I(j)) { + SA[bkt[OArray$get$LOArray$I(s, j)]++] = (j | 0); + } + } +}; + +var SAIS$_induceSAl$LBitVector$AILOArray$AIIIB = SAIS._induceSAl$LBitVector$AILOArray$AIIIB; + +/** + * @param {BitVector} t + * @param {Array.} SA + * @param {OArray} s + * @param {Array.} bkt + * @param {!number} n + * @param {!number} K + * @param {!boolean} end + */ +SAIS._induceSAs$LBitVector$AILOArray$AIIIB = function (t, SA, s, bkt, n, K, end) { + /** @type {!number} */ + var i; + /** @type {!number} */ + var j; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, end); + for (i = n - 1; i >= 0; i--) { + j = SA[i] - 1; + if (j >= 0 && t.get$I(j)) { + SA[-- bkt[OArray$get$LOArray$I(s, j)]] = (j | 0); + } + } +}; + +var SAIS$_induceSAs$LBitVector$AILOArray$AIIIB = SAIS._induceSAs$LBitVector$AILOArray$AIIIB; + +/** + * @param {!string} source + * @return {Array.} + */ +SAIS.make$S = function (source) { + /** @type {Array.} */ + var charCodes; + /** @type {!number} */ + var maxCode; + /** @type {!number} */ + var i; + /** @type {!number} */ + var code; + /** @type {Array.} */ + var SA; + /** @type {OArray} */ + var s; + charCodes = [ ]; + charCodes.length = source.length; + maxCode = 0; + for (i = 0; i < source.length; i++) { + code = source.charCodeAt(i); + charCodes[i] = (code | 0); + maxCode = (code > maxCode ? code : maxCode); + } + SA = [ ]; + SA.length = source.length; + s = ({offset: 0, array: charCodes}); + SAIS$_make$LOArray$AIII(s, SA, source.length, maxCode); + return SA; +}; + +var SAIS$make$S = SAIS.make$S; + +/** + * @param {OArray} s + * @param {Array.} SA + * @param {!number} n + * @param {!number} K + */ +SAIS._make$LOArray$AIII = function (s, SA, n, K) { + /** @type {BitVector} */ + var t; + /** @type {!number} */ + var i; + /** @type {Array.} */ + var bkt; + /** @type {!number} */ + var n1; + /** @type {!number} */ + var name; + /** @type {!number} */ + var prev; + /** @type {undefined|!number} */ + var pos; + /** @type {!boolean} */ + var diff; + /** @type {!number} */ + var d; + /** @type {!number} */ + var j; + /** @type {Array.} */ + var SA1; + /** @type {OArray} */ + var s1; + /** @type {!number} */ + var i$0; + /** @type {!number} */ + var index$0; + t = new BitVector$(); + t.set$IB(n - 2, false); + t.set$IB(n - 1, true); + for (i = n - 3; i >= 0; i--) { + t.set$IB(i, OArray$isS$LOArray$I(s, i) || OArray$compare$LOArray$II(s, i, i + 1) && t.get$I(i + 1)); + } + bkt = [ ]; + bkt.length = K + 1; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, true); + for (i = 0; i < n; i++) { + SA[i] = -1; + } + for (i = 1; i < n; i++) { + if (SAIS$_isLMS$LBitVector$I(t, i)) { + SA[-- bkt[OArray$get$LOArray$I(s, i)]] = (i | 0); + } + } + SAIS$_induceSAl$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, false); + SAIS$_induceSAs$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, true); + n1 = 0; + for (i = 0; i < n; i++) { + i$0 = SA[i]; + if (i$0 > 0 && t.get$I(i$0) && ! t.get$I(i$0 - 1)) { + SA[n1++] = SA[i]; + } + } + for (i = n1; i < n; i++) { + SA[i] = -1; + } + name = 0; + prev = -1; + for (i = 0; i < n1; i++) { + pos = SA[i]; + diff = false; + for (d = 0; d < n; d++) { + if (prev === -1 || ! OArray$compare$LOArray$II(s, pos + d, prev + d) || t.get$I(pos + d) !== t.get$I(prev + d)) { + diff = true; + break; + } else { + if (d > 0 && (SAIS$_isLMS$LBitVector$I(t, pos + d) || SAIS$_isLMS$LBitVector$I(t, prev + d))) { + break; + } + } + } + if (diff) { + name++; + prev = pos; + } + pos = ((pos % 2 === 0 ? pos / 2 : (pos - 1) / 2) | 0); + SA[n1 + pos] = (name - 1 | 0); + } + for ((i = n - 1, j = n - 1); i >= n1; i--) { + if (SA[i] >= 0) { + SA[j--] = SA[i]; + } + } + SA1 = SA; + s1 = ({offset: n - n1, array: SA}); + if (name < n1) { + SAIS$_make$LOArray$AIII(s1, SA1, n1, name - 1); + } else { + for (i = 0; i < n1; i++) { + SA1[OArray$get$LOArray$I(s1, i)] = (i | 0); + } + } + bkt = [ ]; + bkt.length = K + 1; + SAIS$_getBuckets$LOArray$AIIIB(s, bkt, n, K, true); + for ((i = 1, j = 0); i < n; i++) { + if (SAIS$_isLMS$LBitVector$I(t, i)) { + OArray$set$LOArray$II(s1, j++, i); + } + } + for (i = 0; i < n1; i++) { + index$0 = SA1[i]; + SA1[i] = s1.array[index$0 + s1.offset]; + } + for (i = n1; i < n; i++) { + SA[i] = -1; + } + for (i = n1 - 1; i >= 0; i--) { + j = SA[i]; + SA[i] = -1; + SA[-- bkt[OArray$get$LOArray$I(s, j)]] = (j | 0); + } + SAIS$_induceSAl$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, false); + SAIS$_induceSAs$LBitVector$AILOArray$AIIIB(t, SA, s, bkt, n, K, true); +}; + +var SAIS$_make$LOArray$AIII = SAIS._make$LOArray$AIII; + +OktaviaSearch._stemmer = null; +OktaviaSearch._instance = null; +$__jsx_lazy_init(Oktavia, "eof", function () { + return String.fromCharCode(0); +}); +$__jsx_lazy_init(Oktavia, "eob", function () { + return String.fromCharCode(1); +}); +$__jsx_lazy_init(Oktavia, "unknown", function () { + return String.fromCharCode(3); +}); +Binary._base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +$__jsx_lazy_init(Binary, "_base64DecodeChars", function () { + return [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 ]; +}); +$__jsx_lazy_init(Style, "console", function () { + return ({ 'title': [ '\x1B[32m\x1b[4m', '\x1B[39m\x1b[0m' ], 'url': [ '\x1B[34m', '\x1B[39m' ], 'hit': [ '\x1B[4m', '\x1B[0m' ], 'del': [ '\x1B[9m', '\x1B[0m' ], 'summary': [ '\x1B[90m', '\x1B[39m' ] }); +}); +$__jsx_lazy_init(Style, "html", function () { + return ({ 'title': [ '', '' ], 'url': [ '', '' ], 'hit': [ '', '' ], 'del': [ '', '' ], 'summary': [ '', '' ] }); +}); +$__jsx_lazy_init(Style, "ignore", function () { + return ({ 'tilte': [ '', '' ], 'url': [ '', '' ], 'hit': [ '', '' ], 'del': [ '', '' ], 'summary': [ '', '' ] }); +}); +$__jsx_lazy_init(_Common, "buffers", function () { + return [ "comment", "sgmlDecl", "textNode", "tagName", "doctype", "procInstName", "procInstBody", "entity", "attribName", "attribValue", "cdata", "script" ]; +}); +$__jsx_lazy_init(_Common, "EVENTS", function () { + return [ "text", "processinginstruction", "sgmldeclaration", "doctype", "comment", "attribute", "opentag", "closetag", "opencdata", "cdata", "clo_State.CDATA", "error", "end", "ready", "script", "opennamespace", "closenamespace" ]; +}); +_Common.MAX_BUFFER_LENGTH = 65536; +_State.BEGIN = 1; +_State.TEXT = 2; +_State.TEXT_ENTITY = 3; +_State.OPEN_WAKA = 4; +_State.SGML_DECL = 5; +_State.SGML_DECL_QUOTED = 6; +_State.DOCTYPE = 7; +_State.DOCTYPE_QUOTED = 8; +_State.DOCTYPE_DTD = 9; +_State.DOCTYPE_DTD_QUOTED = 10; +_State.COMMENT_STARTING = 11; +_State.COMMENT = 12; +_State.COMMENT_ENDING = 13; +_State.COMMENT_ENDED = 14; +_State.CDATA = 15; +_State.CDATA_ENDING = 16; +_State.CDATA_ENDING_2 = 17; +_State.PROC_INST = 18; +_State.PROC_INST_BODY = 19; +_State.PROC_INST_ENDING = 20; +_State.OPEN_TAG = 21; +_State.OPEN_TAG_SLASH = 22; +_State.ATTRIB = 23; +_State.ATTRIB_NAME = 24; +_State.ATTRIB_NAME_SAW_WHITE = 25; +_State.ATTRIB_VALUE = 26; +_State.ATTRIB_VALUE_QUOTED = 27; +_State.ATTRIB_VALUE_UNQUOTED = 28; +_State.ATTRIB_VALUE_ENTITY_Q = 29; +_State.ATTRIB_VALUE_ENTITY_U = 30; +_State.CLOSE_TAG = 31; +_State.CLOSE_TAG_SAW_WHITE = 32; +_State.SCRIPT = 33; +_State.SCRIPT_ENDING = 34; +$__jsx_lazy_init(_Entities, "_entities", function () { + return ({ "amp": "&", "gt": ">", "lt": "<", "quot": "\"", "apos": "'", "AElig": 198, "Aacute": 193, "Acirc": 194, "Agrave": 192, "Aring": 197, "Atilde": 195, "Auml": 196, "Ccedil": 199, "ETH": 208, "Eacute": 201, "Ecirc": 202, "Egrave": 200, "Euml": 203, "Iacute": 205, "Icirc": 206, "Igrave": 204, "Iuml": 207, "Ntilde": 209, "Oacute": 211, "Ocirc": 212, "Ograve": 210, "Oslash": 216, "Otilde": 213, "Ouml": 214, "THORN": 222, "Uacute": 218, "Ucirc": 219, "Ugrave": 217, "Uuml": 220, "Yacute": 221, "aacute": 225, "acirc": 226, "aelig": 230, "agrave": 224, "aring": 229, "atilde": 227, "auml": 228, "ccedil": 231, "eacute": 233, "ecirc": 234, "egrave": 232, "eth": 240, "euml": 235, "iacute": 237, "icirc": 238, "igrave": 236, "iuml": 239, "ntilde": 241, "oacute": 243, "ocirc": 244, "ograve": 242, "oslash": 248, "otilde": 245, "ouml": 246, "szlig": 223, "thorn": 254, "uacute": 250, "ucirc": 251, "ugrave": 249, "uuml": 252, "yacute": 253, "yuml": 255, "copy": 169, "reg": 174, "nbsp": 160, "iexcl": 161, "cent": 162, "pound": 163, "curren": 164, "yen": 165, "brvbar": 166, "sect": 167, "uml": 168, "ordf": 170, "laquo": 171, "not": 172, "shy": 173, "macr": 175, "deg": 176, "plusmn": 177, "sup1": 185, "sup2": 178, "sup3": 179, "acute": 180, "micro": 181, "para": 182, "middot": 183, "cedil": 184, "ordm": 186, "raquo": 187, "frac14": 188, "frac12": 189, "frac34": 190, "iquest": 191, "times": 215, "divide": 247, "OElig": 338, "oelig": 339, "Scaron": 352, "scaron": 353, "Yuml": 376, "fnof": 402, "circ": 710, "tilde": 732, "Alpha": 913, "Beta": 914, "Gamma": 915, "Delta": 916, "Epsilon": 917, "Zeta": 918, "Eta": 919, "Theta": 920, "Iota": 921, "Kappa": 922, "Lambda": 923, "Mu": 924, "Nu": 925, "Xi": 926, "Omicron": 927, "Pi": 928, "Rho": 929, "Sigma": 931, "Tau": 932, "Upsilon": 933, "Phi": 934, "Chi": 935, "Psi": 936, "Omega": 937, "alpha": 945, "beta": 946, "gamma": 947, "delta": 948, "epsilon": 949, "zeta": 950, "eta": 951, "theta": 952, "iota": 953, "kappa": 954, "lambda": 955, "mu": 956, "nu": 957, "xi": 958, "omicron": 959, "pi": 960, "rho": 961, "sigmaf": 962, "sigma": 963, "tau": 964, "upsilon": 965, "phi": 966, "chi": 967, "psi": 968, "omega": 969, "thetasym": 977, "upsih": 978, "piv": 982, "ensp": 8194, "emsp": 8195, "thinsp": 8201, "zwnj": 8204, "zwj": 8205, "lrm": 8206, "rlm": 8207, "ndash": 8211, "mdash": 8212, "lsquo": 8216, "rsquo": 8217, "sbquo": 8218, "ldquo": 8220, "rdquo": 8221, "bdquo": 8222, "dagger": 8224, "Dagger": 8225, "bull": 8226, "hellip": 8230, "permil": 8240, "prime": 8242, "Prime": 8243, "lsaquo": 8249, "rsaquo": 8250, "oline": 8254, "frasl": 8260, "euro": 8364, "image": 8465, "weierp": 8472, "real": 8476, "trade": 8482, "alefsym": 8501, "larr": 8592, "uarr": 8593, "rarr": 8594, "darr": 8595, "harr": 8596, "crarr": 8629, "lArr": 8656, "uArr": 8657, "rArr": 8658, "dArr": 8659, "hArr": 8660, "forall": 8704, "part": 8706, "exist": 8707, "empty": 8709, "nabla": 8711, "isin": 8712, "notin": 8713, "ni": 8715, "prod": 8719, "sum": 8721, "minus": 8722, "lowast": 8727, "radic": 8730, "prop": 8733, "infin": 8734, "ang": 8736, "and": 8743, "or": 8744, "cap": 8745, "cup": 8746, "int": 8747, "there4": 8756, "sim": 8764, "cong": 8773, "asymp": 8776, "ne": 8800, "equiv": 8801, "le": 8804, "ge": 8805, "sub": 8834, "sup": 8835, "nsub": 8836, "sube": 8838, "supe": 8839, "oplus": 8853, "otimes": 8855, "perp": 8869, "sdot": 8901, "lceil": 8968, "rceil": 8969, "lfloor": 8970, "rfloor": 8971, "lang": 9001, "rang": 9002, "loz": 9674, "spades": 9824, "clubs": 9827, "hearts": 9829, "diams": 9830 }); +}); +BitVector.SMALL_BLOCK_SIZE = 32; +BitVector.LARGE_BLOCK_SIZE = 256; +BitVector.BLOCK_RATE = 8; +$__jsx_lazy_init(BurrowsWheelerTransform, "END_MARKER", function () { + return String.fromCharCode(0); +}); +var $__jsx_classMap = { + "tool/web/oktavia-search.jsx": { + _Result: _Result, + _Result$SSSI: _Result$SSSI, + _Proposal: _Proposal, + _Proposal$SSI: _Proposal$SSI, + OktaviaSearch: OktaviaSearch, + OktaviaSearch$I: OktaviaSearch$I, + _Main: _Main, + _Main$: _Main$ + }, + "src/oktavia.jsx": { + Oktavia: Oktavia, + Oktavia$: Oktavia$ + }, + "src/binary-util.jsx": { + Binary: Binary, + Binary$: Binary$, + LoadedStringResult: LoadedStringResult, + LoadedStringResult$SI: LoadedStringResult$SI, + LoadedStringListResult: LoadedStringListResult, + LoadedStringListResult$SI: LoadedStringListResult$SI, + LoadedStringListMapResult: LoadedStringListMapResult, + LoadedStringListMapResult$SI: LoadedStringListMapResult$SI, + LoadedNumberListResult: LoadedNumberListResult, + LoadedNumberListResult$SI: LoadedNumberListResult$SI, + CompressionReport: CompressionReport, + CompressionReport$: CompressionReport$ + }, + "src/query.jsx": { + Query: Query, + Query$: Query$ + }, + "src/query-string-parser.jsx": { + QueryStringParser: QueryStringParser, + QueryStringParser$: QueryStringParser$ + }, + "src/search-result.jsx": { + Proposal: Proposal, + Proposal$II: Proposal$II, + Position: Position, + Position$SIB: Position$SIB, + SearchUnit: SearchUnit, + SearchUnit$I: SearchUnit$I, + SingleResult: SingleResult, + SingleResult$: SingleResult$, + SingleResult$SBB: SingleResult$SBB, + SearchSummary: SearchSummary, + SearchSummary$: SearchSummary$, + SearchSummary$LOktavia$: SearchSummary$LOktavia$ + }, + "src/style.jsx": { + Style: Style, + Style$S: Style$S, + _HTMLHandler: _HTMLHandler, + _HTMLHandler$HASB: _HTMLHandler$HASB + }, + "src/stemmer/stemmer.jsx": { + Stemmer: Stemmer, + Stemmer$: Stemmer$ + }, + "src/metadata.jsx": { + Metadata: Metadata, + Metadata$LOktavia$: Metadata$LOktavia$, + Section: Section, + Section$LOktavia$: Section$LOktavia$, + Splitter: Splitter, + Splitter$LOktavia$: Splitter$LOktavia$, + Splitter$LOktavia$S: Splitter$LOktavia$S, + Table: Table, + Table$LOktavia$AS: Table$LOktavia$AS, + Block: Block, + Block$LOktavia$: Block$LOktavia$ + }, + "src/fm-index.jsx": { + FMIndex: FMIndex, + FMIndex$: FMIndex$ + }, + "src/sax.jsx": { + Tag: Tag, + Tag$S: Tag$S, + _Common: _Common, + _Common$: _Common$, + _State: _State, + _State$: _State$, + SAXHandler: SAXHandler, + SAXHandler$: SAXHandler$, + SAXParser: SAXParser, + SAXParser$LSAXHandler$: SAXParser$LSAXHandler$, + SAXParser$LSAXHandler$B: SAXParser$LSAXHandler$B, + Char: Char, + Char$: Char$, + _Entities: _Entities, + _Entities$: _Entities$ + }, + "src/bit-vector.jsx": { + BitVector: BitVector, + BitVector$: BitVector$ + }, + "src/wavelet-matrix.jsx": { + WaveletMatrix: WaveletMatrix, + WaveletMatrix$: WaveletMatrix$ + }, + "src/burrows-wheeler-transform.jsx": { + BurrowsWheelerTransform: BurrowsWheelerTransform, + BurrowsWheelerTransform$: BurrowsWheelerTransform$ + }, + "src/sais.jsx": { + OArray: OArray, + OArray$AI: OArray$AI, + OArray$AII: OArray$AII, + SAIS: SAIS, + SAIS$: SAIS$ + } +}; + + +/** + * launches _Main.main(:string[]):void invoked by jsx --run|--executable + */ +JSX.runMain = function (sourceFile, args) { + var module = JSX.require(sourceFile); + if (! module) { + throw new ReferenceError("entry point module not found in " + sourceFile); + } + if (! module._Main) { + throw new ReferenceError("entry point _Main not found in " + sourceFile); + } + if (! module._Main.main$AS) { + throw new ReferenceError("entry point _Main.main(:string[]):void not found in " + sourceFile); + } + module._Main.main$AS(args); +}; + +/** + * launches _Test#test*():void invoked by jsx --test + */ +JSX.runTests = function (sourceFile, tests) { + var module = JSX.require(sourceFile); + var testClass = module._Test$; + + if (!testClass) return; // skip if there's no test class + + if(tests.length === 0) { + var p = testClass.prototype; + for (var m in p) { + if (p[m] instanceof Function + && /^test.*[$]$/.test(m)) { + tests.push(m); + } + } + } + else { // set as process arguments + tests = tests.map(function (name) { + return name + "$"; // mangle for function test*():void + }); + } + + var testCase = new testClass(); + + if (testCase.beforeClass$AS != null) + testCase.beforeClass$AS(tests); + + for (var i = 0; i < tests.length; ++i) { + (function (method) { + if (method in testCase) { + testCase.run$SF$V$(method, function() { testCase[method](); }); + } + else { + throw new ReferenceError("No such test method: " + method); + } + }(tests[i])); + } + + if (testCase.afterClass$ != null) + testCase.afterClass$(); +}; +/** + * call a function on load/DOMContentLoaded + */ +function $__jsx_onload (event) { + window.removeEventListener("load", $__jsx_onload); + document.removeEventListener("DOMContentLoaded", $__jsx_onload); + JSX.runMain("tool/web/oktavia-search.jsx", []) +} + +window.addEventListener("load", $__jsx_onload); +document.addEventListener("DOMContentLoaded", $__jsx_onload); + +})(JSX); diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/searchstyle.css b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/searchstyle.css new file mode 100644 index 000000000..a3d51d436 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_static/searchstyle.css @@ -0,0 +1,174 @@ +form.oktavia_form { + position: relative; + display: block; + width: 240px; + height: 25px; +} + +input.oktavia_search { + -webkit-appearance: searchfield; + -webkit-user-select: text; + cursor: auto; + background-color: white; + color: black; + line-height: normal; + display: inline-block; + padding: 1px; + text-align: start; + margin: 2px 0px 2px 0px; + padding: 1px; +} + +div.oktavia_searchresult_box { + display: none; + position: absolute; + width: 500px; + padding: 10px; + right: 10px; + background-color: #ffffff; + -moz-border-radius: 8px; + border-radius: 8px; + -moz-box-shadow: 3px 3px 5px 5px #b5b2b2; + box-shadow: 3px 3px 5px 5px #b5b2b2; + opacity: 0.95; + z-index: 100000; +} + +div.oktavia_close_search_box { + position: absolute; + top: 10px; + right: 10px; + width: 20px; + height: 20px; + text-align: center; + vertical-align: middle; + color: #666; +} + +div.oktavia_close_search_box:hover { + background-color: #d1e8ff; + border: 1px solid gray; +} + +div.oktavia_close_search_box:active { + background-color: #b4c8db; + border: 1px solid gray; +} + + +div.oktavia_searchresult_summary { + color: #444; +} + +div.oktavia_searchresult .entry { + margin: 10px 10px 10px 10px; + color: black; +} + +div.oktavia_searchresult .entry .title { + font-size: normal; +} + +div.oktavia_searchresult .entry .title a:link { + color: #0000EE; + text-decoration: underline; +} + +div.oktavia_searchresult .entry .title a:visited { + color: #551A8B; + text-decoration: underline; +} + +div.oktavia_searchresult .entry .title a:hover { +} + +div.oktavia_searchresult .entry .title a:active { + color: #FF0000; + text-decoration: underline; +} + +div.oktavia_searchresult .entry .url { + font-size: 14px!important; + color: #093; + font-style: normal; + font-family: arial,sans-serif; +} + +div.oktavia_searchresult .entry .resultcontent { + font-size: small; + font-family: arial,sans-serif; + color: #222; +} + +div.oktavia_searchresult .entry .resultcontent .hit { + font-weight: bolder; + text-decoration: underline; +} + +div.oktavia_searchresult .proposal { + margin: 10px 10px 10px 10px; + color: #222; + font-size: small; + font-family: arial,sans-serif; +} + +div.oktavia_searchresult .proposal .option { + background-color: #e8f4ff; + border-bottom: 1px solid blue; +} + +div.oktavia_searchresult .proposal .option:hover { + background-color: #d1e8ff; +} + +div.oktavia_searchresult .proposal .option:active { + background-color: #b4c8db; +} + +div.oktavia_searchresult_nav span.leader { + color: #228; +} + +div.oktavia_searchresult_nav span.page { + background-color: #ffffff; + border: 1px solid #888888; + padding: 5px; + margin: 5px; + color: #222; +} + +div.oktavia_searchresult_nav span.page:hover { + background-color: #d1e8ff; + border: 1px solid #7d98ff; +} + +div.oktavia_searchresult_nav span.page:active { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_nav span.selected { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_nav span.selected:hover { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_nav span.selected:active { + background-color: #b4c8db; + border: 1px solid #6c83db; +} + +div.oktavia_searchresult_box span.pr { + position: absolute; + right: 10px; + bottom: 10px; + color: #555; +} + +.highlighted { + background-color: #fbe54e; +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_templates/oktaviasearch.html b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_templates/oktaviasearch.html new file mode 100644 index 000000000..30bf86267 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/templates/tinkerer/_templates/oktaviasearch.html @@ -0,0 +1,8 @@ +
+

Search by Oktavia

+ + + +
+
+ diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-binary-util.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-binary-util.jsx new file mode 100644 index 000000000..25882e340 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-binary-util.jsx @@ -0,0 +1,190 @@ +import "test-case.jsx"; +import "binary-util.jsx"; + +class _Test extends TestCase +{ + function test_16bit_number() : void + { + this.expect(Binary.load16bitNumber(Binary.dump16bitNumber(0), 0)).toBe(0); + this.expect(Binary.load16bitNumber(Binary.dump16bitNumber(65535), 0)).toBe(65535); + this.expect(Binary.load16bitNumber(Binary.dump16bitNumber(65536), 0)).notToBe(65536); + } + + function test_32bit_number() : void + { + this.expect(Binary.load32bitNumber(Binary.dump32bitNumber(0), 0)).toBe(0); + this.expect(Binary.load32bitNumber(Binary.dump32bitNumber(4294967295), 0)).toBe(4294967295); + this.expect(Binary.load32bitNumber(Binary.dump32bitNumber(4294967296), 0)).notToBe(4294967296); + } + + function test_string() : void + { + var str = Binary.loadString(Binary.dumpString('hello world'), 0); + this.expect(str.result).toBe('hello world'); + + // 7bit safe charactes will be compressed + this.expect(Binary.dumpString('hello world').length).toBeLE('hello world'.length); + this.expect(Binary.dumpString('').length).toBe(''.length + 1); + + // 7bit unsafe charactes will not be compressed + this.expect(Binary.dumpString('\u1111\u1111').length).toBe('\u1111\u1111'.length + 1); + } + + function test_string_list() : void + { + var list = Binary.loadStringList(Binary.dumpStringList(['hello', 'world']), 0); + this.expect(list.result[0]).toBe('hello'); + this.expect(list.result[1]).toBe('world'); + + var list = Binary.loadStringList(Binary.dumpStringList(['\u1111', '\u2222']), 0); + this.expect(list.result[0]).toBe('\u1111'); + this.expect(list.result[1]).toBe('\u2222'); + } + + function test_string_list_map() : void + { + var src = {'hello': ['HELLO'], 'world': ['WORLD']}; + var list = Binary.loadStringListMap(Binary.dumpStringListMap(src), 0); + this.expect(list.result['hello'][0]).toBe('HELLO'); + this.expect(list.result['world'][0]).toBe('WORLD'); + } + + function test_32bit_number_list_blank() : void + { + var list = [0, 0, 0, 0, 0, 0]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 + 1); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(6); + this.expect(loaded.result[0]).toBe(0); + this.expect(loaded.result[5]).toBe(0); + this.expect(loaded.offset).toBe(2 + 1); + } + + function test_32bit_number_list_non_blank() : void + { + var list = [1, 1, 1, 1, 1, 1]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 * 6 + 2 + 1); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(6); + this.expect(loaded.result[0]).toBe(1); + this.expect(loaded.result[5]).toBe(1); + this.expect(loaded.offset).toBe(2 * 6 + 2 + 1); + } + + function test_32bit_number_list_zebra() : void + { + var list = [1, 0, 1, 0, 1, 0]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 * 3 + 2 + 1); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(6); + this.expect(loaded.result[0]).toBe(1); + this.expect(loaded.result[1]).toBe(0); + this.expect(loaded.result[2]).toBe(1); + this.expect(loaded.result[3]).toBe(0); + this.expect(loaded.result[4]).toBe(1); + this.expect(loaded.result[5]).toBe(0); + this.expect(loaded.offset).toBe(2 * 3 + 2 + 1); + } + + function test_32bit_number_list_combo1() : void + { + // non-blank + blank + var list = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 + 1 + 2 * 17 + 1); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(list.length); + this.expect(loaded.result[0]).toBe(1); + this.expect(loaded.result[15]).toBe(1); + this.expect(loaded.result[17]).toBe(0); + this.expect(loaded.result[19]).toBe(0); + this.expect(loaded.offset).toBe(2 + 1 + 2 * 17 + 1); + } + + function test_32bit_number_list_combo2() : void + { + // blank + non-blank + var list = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 + 1 + 1 + 2 * 17); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(list.length); + this.expect(loaded.result[0]).toBe(0); + this.expect(loaded.result[2]).toBe(0); + this.expect(loaded.result[3]).toBe(1); + this.expect(loaded.result[19]).toBe(1); + this.expect(loaded.offset).toBe(2 + 1 + 1 + 2 * 17); + } + + function test_32bit_number_list_combo3() : void + { + // non-blank + zebra + var list = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 + 1 + 2 * 16 + 1 + 1 + 2 * 3); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(list.length); + this.expect(loaded.result[0]).toBe(1); + this.expect(loaded.result[9]).toBe(1); + this.expect(loaded.result[16]).toBe(0); + this.expect(loaded.result[18]).toBe(1); + this.expect(loaded.offset).toBe(2 + 1 + 2 * 16 + 1 + 1 + 2 * 3); + } + + function test_32bit_number_list_combo4() : void + { + // zebra + non-block + var list = [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 + 1 + 2 * 11 + 1 + 2 * 16); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(list.length); + this.expect(loaded.result[0]).toBe(1); + this.expect(loaded.result[14]).toBe(0); + this.expect(loaded.result[15]).toBe(1); + this.expect(loaded.result[30]).toBe(2); + this.expect(loaded.offset).toBe(2 + 1 + 2 * 11 + 1 + 2 * 16); + } + + function test_32bit_number_list_combo5() : void + { + // zero + zebra + var list = [0, 0, 0, 0, 0, 0, 1]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 + 1 + 1 + 2); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(7); + this.expect(loaded.result[0]).toBe(0); + this.expect(loaded.result[6]).toBe(1); + this.expect(loaded.offset).toBe(2 + 1 + 1 + 2); + } + + function test_32bit_number_list_combo6() : void + { + // zebra + zero + var list = [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + var dumped = Binary.dump32bitNumberList(list); + this.expect(dumped.length).toBe(2 + 1 + 2 * 12 + 1); + var loaded = Binary.load32bitNumberList(dumped, 0); + this.expect(loaded.result.length).toBe(list.length); + this.expect(loaded.result[0]).toBe(1); + this.expect(loaded.result[14]).toBe(1); + this.expect(loaded.result[15]).toBe(0); + this.expect(loaded.result[23]).toBe(0); + this.expect(loaded.offset).toBe(2 + 1 + 2 * 12 + 1); + } + + function test_base64_encode_decode() : void + { + var allChars = [] : string[]; + for (var i = 256; i < 65536; i++) + { + allChars.push(String.fromCharCode(i)); + } + var allCharSource = allChars.join(''); + this.expect(Binary.base64decode(Binary.base64encode(allCharSource))).toBe(allCharSource); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-bit-vector.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-bit-vector.jsx new file mode 100644 index 000000000..18f80b645 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-bit-vector.jsx @@ -0,0 +1,131 @@ +import "test-case.jsx"; +import "bit-vector.jsx"; +import "console.jsx"; + +class _Test extends TestCase +{ + var src_values : int[]; + var bv0 : BitVector; + var bv1 : BitVector; + + override function setUp () : void + { + this.bv0 = new BitVector(); + this.bv1 = new BitVector(); + + this.src_values = [0, 511, 512, 1000, 2000, 3000] : int[]; + + for (var i = 0; i <= this.src_values[this.src_values.length - 1]; i++) + { + this.bv0.set(i, true); + } + + for (var i = 0; i < this.src_values.length; i++) + { + var v = this.src_values[i]; + this.bv1.set(v, true); + this.bv0.set(v, false); + } + this.bv1.build(); + this.bv0.build(); + } + + function test_size () : void + { + this.expect(this.bv1.size()).toBe(this.src_values[this.src_values.length - 1] + 1); // == 3001 + this.expect(this.bv1.size(true)).toBe(this.src_values.length); // == 6 + this.expect(this.bv0.size()).toBe(this.src_values[this.src_values.length - 1] + 1); // == 3001 + this.expect(this.bv0.size(false)).toBe(this.src_values.length); // == 6 + } + + function test_get () : void + { + for (var i = 0; i < this.src_values.length; i++) + { + var v = this.src_values[i]; + this.expect(this.bv1.get(v)).toBe(true); + this.expect(this.bv0.get(v)).toBe(false); + } + } + + function test_rank () : void + { + for (var i = 0; i < this.src_values.length; i++) + { + var v = this.src_values[i]; + this.expect(this.bv1.rank(v, true)).toBe(i); + this.expect(this.bv0.rank(v, false)).toBe(i); + } + } + + function test_select () : void + { + for (var i = 0; i < this.src_values.length; i++) + { + var v = this.src_values[i]; + this.expect(this.bv1.select(i, true)).toBe(v); + this.expect(this.bv0.select(i, false)).toBe(v); + } + } + + function test_load_dump_and_size () : void + { + console.log('dump1'); + var dump1 = this.bv1.dump(); + console.log('dump0'); + var dump0 = this.bv0.dump(); + console.log('load1'); + this.bv1.load(dump1); + console.log('load0'); + this.bv0.load(dump0); + + this.expect(this.bv1.size()).toBe(this.src_values[this.src_values.length - 1] + 1); // == 3001 + this.expect(this.bv1.size(true)).toBe(this.src_values.length); // == 6 + this.expect(this.bv0.size()).toBe(this.src_values[this.src_values.length - 1] + 1); // == 3001 + this.expect(this.bv0.size(false)).toBe(this.src_values.length); // == 6 + } + + function test_load_dump_and_get () : void + { + var dump1 = this.bv1.dump(); + var dump0 = this.bv0.dump(); + this.bv1.load(dump1); + this.bv0.load(dump0); + + for (var i = 0; i < this.src_values.length; i++) + { + var v = this.src_values[i]; + this.expect(this.bv1.get(v)).toBe(true); + this.expect(this.bv0.get(v)).toBe(false); + } + } + + function test_load_dump_and_rank () : void + { + var dump1 = this.bv1.dump(); + var dump0 = this.bv0.dump(); + this.bv1.load(dump1); + this.bv0.load(dump0); + + for (var i = 0; i < this.src_values.length; i++) + { + var v = this.src_values[i]; + this.expect(this.bv1.rank(v, true)).toBe(i); + this.expect(this.bv0.rank(v, false)).toBe(i); + } + } + + function test_load_dump_and_select () : void + { + var dump1 = this.bv1.dump(); + var dump0 = this.bv0.dump(); + this.bv1.load(dump1); + this.bv0.load(dump0); + for (var i = 0; i < this.src_values.length; i++) + { + var v = this.src_values[i]; + this.expect(this.bv1.select(i, true)).toBe(v); + this.expect(this.bv0.select(i, false)).toBe(v); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-burrows-wheeler-transform.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-burrows-wheeler-transform.jsx new file mode 100644 index 000000000..6a033e589 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-burrows-wheeler-transform.jsx @@ -0,0 +1,24 @@ +/** + * This is a JSX version of shellinford library: + * https://code.google.com/p/shellinford/ + * + * License: http://shibu.mit-license.org/ + */ + +import "test-case.jsx"; +import "burrows-wheeler-transform.jsx"; + +class _Test extends TestCase +{ + var bwt : BurrowsWheelerTransform; + override function setUp() : void + { + this.bwt = new BurrowsWheelerTransform(); + this.bwt.build('abracadabra' + BurrowsWheelerTransform.END_MARKER); + } + + function test_get() : void + { + this.expect(this.bwt.get("$")).toBe("ard$rcaaaabb"); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-fm-index.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-fm-index.jsx new file mode 100644 index 000000000..80035a61f --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-fm-index.jsx @@ -0,0 +1,250 @@ +/** + * This is a JSX version of shellinford library: + * https://code.google.com/p/shellinford/ + * + * License: http://shibu.mit-license.org/ + */ + +import "test-case.jsx"; +import "fm-index.jsx"; + +class Pair +{ + var first : string; + var second : int; + function constructor (_first : string, _second : int) + { + this.first = _first; + this.second = _second; + } +} + +class _Test extends TestCase +{ + var str : string; + var rd : Map.; + var pd : int[]; + var sd : string[]; + var didd : int[]; + var docd : string[]; + var fm : FMIndex; + var end_marker : string; + + override function setUp () : void + { + this.str = ""; + this.sd = [] : string[]; + this.rd = {} : Map.; + this.pd = [] : int[]; + this.didd = [] : int[]; + this.docd = [] : string[]; + this.fm = new FMIndex(); + this.end_marker = String.fromCharCode(1); + + this.docd.push("abracadabra"); + this.docd.push("mississippi"); + this.docd.push("abracadabra mississippi"); + + var did = 0; + for (var i in this.docd) + { + var doc = this.docd[i]; + this.str += doc; + for (var j = 0; j < doc.length; j++){ + this.didd.push(did); + } + this.fm.push(doc); + did++; + } + + this.didd.push(did); + this.str += this.end_marker; + this.fm.build(this.end_marker, 3, false); + + for (var i = 0; i < this.str.length; i++) + { + for (var j = 1; j <= (this.str.length - i); j++) + { + var s = this.str.slice(i, i + j); + if (this.rd[s] == null) + { + this.rd[s] = 1; + } + else + { + this.rd[s]++; + } + } + } + + var v = [] : Pair[]; + for (var i = 0; i < this.str.length; i++) + { + var s = this.str.slice(i) + this.str.slice(0, i); + v.push(new Pair(s, i)); + } + v.sort(function (a: Pair, b: Pair) : number { + if (a.first < b.first) + { + return -1; + } + else if (a.first > b.first) + { + return 1; + } + return a.second - b.second; + }); + for (var i in v) + { + this.pd.push(v[i].second); + } + for (var i = 0; i < this.str.length; i++) + { + this.sd.push(this.str.slice(i)); + } + } + + function test_size () : void + { + this.expect(this.fm.size()).toBe(this.str.length); + } + + function test_getRows () : void + { + for (var i = 0; i < this.fm.size(); i++) + { + for (var j = i + 1; j < this.fm.size(); j++) + { + var s = this.str.slice(i, j); + this.expect(this.fm.getRows(s)).toBe(this.rd[s]); + } + } + } + + function test_getPosition () : void + { + for (var i = 0; i < this.fm.size(); i++) + { + this.expect(this.fm.getPosition(i)).toBe(this.pd[i]); + } + } + + function test_getSubstring () : void + { + for (var i = 0; i < this.fm.size(); i++) + { + this.expect(this.fm.getSubstring(i, this.fm.size())).toBe(this.sd[i]); + } + } + + function test_getSubstring2 () : void + { + this.fm = new FMIndex(); + this.fm.push("abracadabra"); + this.fm.push("mississippi"); + this.fm.push("abracadabra mississippi"); + this.fm.build(this.end_marker, 3, false); + this.expect(this.fm.getSubstring(0, 11)).toBe('abracadabra'); + this.expect(this.fm.getSubstring(11, 11)).toBe('mississippi'); + this.expect(this.fm.getSubstring(22, 23)).toBe('abracadabra mississippi'); + } + + function test_getPosition_boundary () : void + { + try + { + this.fm.getPosition(this.fm.size()); + this.fail("fm.getPosition()"); + } + catch (e : Error) + { + } + } + + function test_getSubstring_boundary () : void + { + try + { + this.fm.getSubstring(this.fm.size(), 0); + this.fail("fm.getSubstring()"); + } + catch (e : Error) + { + } + } + + function test_dump_load_and_size () : void + { + var dump = this.fm.dump(); + this.fm.load(dump); + + this.expect(this.fm.size()).toBe(this.str.length); + } + + function test_dump_load_and_getRows () : void + { + var dump = this.fm.dump(); + this.fm.load(dump); + + for (var i = 0; i < this.fm.size(); i++) + { + for (var j = i + 1; j < this.fm.size(); j++) + { + var s = this.str.slice(i, j); + this.expect(this.fm.getRows(s)).toBe(this.rd[s]); + } + } + } + + function test_dump_load_and_getPosition () : void + { + var dump = this.fm.dump(); + this.fm.load(dump); + + for (var i = 0; i < this.fm.size(); i++) + { + this.expect(this.fm.getPosition(i)).toBe(this.pd[i]); + } + } + + function test_dump_load_and_getSubstring () : void + { + var dump = this.fm.dump(); + this.fm.load(dump); + + for (var i = 0; i < this.fm.size(); i++) + { + this.expect(this.fm.getSubstring(i, this.fm.size())).toBe(this.sd[i]); + } + } + + function test_dump_load_and_getPosition_boundary () : void + { + var dump = this.fm.dump(); + this.fm.load(dump); + + try + { + this.fm.getPosition(this.fm.size()); + this.fail("fm.getPosition()"); + } + catch (e : Error) + { + } + } + + function test_dump_load_and_getSubstring_boundary () : void + { + var dump = this.fm.dump(); + this.fm.load(dump); + + try + { + this.fm.getSubstring(this.fm.size(), 0); + this.fail("fm.getSubstring()"); + } + catch (e : Error) + { + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-getopt.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-getopt.jsx new file mode 100644 index 000000000..63731b924 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-getopt.jsx @@ -0,0 +1,84 @@ +import "test-case.jsx"; +import "getopt.jsx"; + +class _Test extends TestCase +{ + function test_empty () : void + { + var parser = new BasicParser('', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_silent () : void + { + var parser = new BasicParser(':', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_args_without_param_01 () : void + { + var parser = new BasicParser(':l', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_args_without_param_02 () : void + { + var parser = new BasicParser(':l:', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_args_without_param_03 () : void + { + var parser = new BasicParser(':las', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_args_without_param_04 () : void + { + var parser = new BasicParser(':l:a:s:', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_long_args_without_param () : void + { + var parser = new BasicParser(':l:(long)', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_args () : void + { + var parser = new BasicParser(':l:(long)', ['-l', 'arg1', '--long=q', 'b', '--long', 'foo']); + var opt = parser.getopt(); + this.expect(opt.option).toBe('l'); + this.expect(opt.optarg).toBe('arg1'); + opt = parser.getopt(); + this.expect(opt.option).toBe('l'); + this.expect(opt.optarg).toBe('q'); + opt = parser.getopt(); + this.expect(opt.option).toBe('b'); + opt = parser.getopt(); + this.expect(opt.option).toBe('--long'); + opt = parser.getopt(); + this.expect(opt.option).toBe('foo'); + } + + function test_aliased_long_args_without_param_01 () : void + { + var parser = new BasicParser(':l:(long)(longer)', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_aliased_long_args_without_param_02 () : void + { + var parser = new BasicParser(':la:r(recurse)(recur)f:(file)(filename)q', [] : string[]); + this.expect(parser.getopt()).toBe(null); + } + + function test_extra_options () : void + { + var parser = new BasicParser('la:r(recurse)(recur)f:(file)(filename)q', ['extra1', 'extra2']); + this.expect(parser.getopt().option).toBe('extra1'); + this.expect(parser.getopt().option).toBe('extra2'); + this.expect(parser.getopt()).toBe(null); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-block.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-block.jsx new file mode 100644 index 000000000..f5f562c7d --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-block.jsx @@ -0,0 +1,226 @@ +/** + * This is a JSX version of shellinford library: + * https://code.google.com/p/shellinford/ + * + * License: http://shibu.mit-license.org/ + */ + +import "test-case.jsx"; +import "oktavia.jsx"; +import "metadata.jsx"; + +class _Test extends TestCase +{ + var oktavia : Oktavia; + var block : Block; + + override function setUp () : void + { + this.oktavia = new Oktavia(); + this.block = this.oktavia.addBlock('document'); + this.oktavia.addWord("abracadabra"); + this.block.startBlock("river"); + this.oktavia.addWord("mississippi"); + this.block.endBlock(); + this.oktavia.addWord("abracadabra mississippi"); + this.oktavia.build(); + } + + function test_doc_sizes () : void + { + this.expect(this.block.size()).toBe(1); + } + + function test_in_block () : void + { + this.expect(this.block.inBlock(0)).toBe(false); + this.expect(this.block.inBlock(10)).toBe(false); + this.expect(this.block.inBlock(11)).toBe(true); + this.expect(this.block.inBlock(21)).toBe(true); + this.expect(this.block.inBlock(22)).toBe(false); + this.expect(this.block.inBlock(44)).toBe(false); + } + + function test_in_block_boundary () : void + { + try + { + this.block.inBlock(-1); + this.fail("fm.inBlock() 1"); + } + catch (e : Error) + { + } + try + { + this.block.inBlock(45); + this.fail("fm.inBlock() 2"); + } + catch (e : Error) + { + } + } + + function test_get_block_content () : void + { + this.expect(this.block.getBlockContent(11)).toBe("mississippi"); + } + + function test_get_block_content_boundary () : void + { + try + { + this.block.getBlockContent(45); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.block.getBlockContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } + + function test_get_block_name () : void + { + this.expect(this.block.getBlockName(11)).toBe("river"); + } + + function test_get_block_name_boundary () : void + { + try + { + this.block.getBlockName(45); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + try + { + this.block.getBlockName(-1); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + } + + function test_dump_load_and_doc_sizes () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.block = this.oktavia.getBlock('document'); + + this.expect(this.block.size()).toBe(1); + } + + function test_load_dump_and_in_block () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.block = this.oktavia.getBlock('document'); + + this.expect(this.block.inBlock(0)).toBe(false); + this.expect(this.block.inBlock(10)).toBe(false); + this.expect(this.block.inBlock(11)).toBe(true); + this.expect(this.block.inBlock(21)).toBe(true); + this.expect(this.block.inBlock(22)).toBe(false); + this.expect(this.block.inBlock(44)).toBe(false); + } + + function test_load_dump_and_in_block_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.block = this.oktavia.getBlock('document'); + + try + { + this.block.inBlock(-1); + this.fail("fm.inBlock() 1"); + } + catch (e : Error) + { + } + try + { + this.block.inBlock(45); + this.fail("fm.inBlock() 2"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_get_block_content () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.block = this.oktavia.getBlock('document'); + + this.expect(this.block.getBlockContent(11)).toBe("mississippi"); + } + + function test_load_dump_and_get_block_content_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.block = this.oktavia.getBlock('document'); + + try + { + this.block.getBlockContent(45); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.block.getBlockContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_get_block_name () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.block = this.oktavia.getBlock('document'); + + this.expect(this.block.getBlockName(11)).toBe("river"); + } + + function test_load_dump_and_get_block_name_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.block = this.oktavia.getBlock('document'); + + try + { + this.block.getBlockName(45); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + try + { + this.block.getBlockName(-1); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-section.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-section.jsx new file mode 100644 index 000000000..0c37afeaa --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-section.jsx @@ -0,0 +1,235 @@ +/** + * This is a JSX version of shellinford library: + * https://code.google.com/p/shellinford/ + * + * License: http://shibu.mit-license.org/ + */ + +import "test-case.jsx"; +import "oktavia.jsx"; +import "metadata.jsx"; + +class _Test extends TestCase +{ + var oktavia : Oktavia; + var section : Section; + + override function setUp () : void + { + this.oktavia = new Oktavia(); + this.section = this.oktavia.addSection('document'); + this.oktavia.addWord("abracadabra"); + this.section.setTail("doc1"); + this.oktavia.addWord("mississippi"); + this.section.setTail("doc2"); + this.oktavia.addWord("abracadabra mississippi"); + this.section.setTail("doc3"); + this.oktavia.build(25, false); + } + + function test_doc_sizes () : void + { + this.expect(this.section.size()).toBe(3); + } + + function test_get_section_index () : void + { + this.expect(this.section.getSectionIndex(0)).toBe(0); + this.expect(this.section.getSectionIndex(10)).toBe(0); + this.expect(this.section.getSectionIndex(11)).toBe(1); + this.expect(this.section.getSectionIndex(21)).toBe(1); + this.expect(this.section.getSectionIndex(22)).toBe(2); + this.expect(this.section.getSectionIndex(44)).toBe(2); + } + + function test_get_section_index_boundary () : void + { + try + { + this.section.getSectionIndex(-1); + this.fail("fm.getSectionIndex()"); + } + catch (e : Error) + { + } + try + { + this.section.getSectionIndex(45); + this.fail("fm.getSectionIndex()"); + } + catch (e : Error) + { + } + } + + function test_get_section_content () : void + { + this.expect(this.section.getContent(0)).toBe("abracadabra"); + this.expect(this.section.getContent(1)).toBe("mississippi"); + this.expect(this.section.getContent(2)).toBe("abracadabra mississippi"); + } + + function test_get_section_content_boundary () : void + { + try + { + this.section.getContent(3); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.section.getContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } + + function test_get_section_name () : void + { + this.expect(this.section.getName(0)).toBe("doc1"); + this.expect(this.section.getName(1)).toBe("doc2"); + this.expect(this.section.getName(2)).toBe("doc3"); + } + + function test_get_section_name_boundary () : void + { + try + { + this.section.getName(3); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + try + { + this.section.getName(-1); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_doc_sizes () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.section = this.oktavia.getSection('document'); + + this.expect(this.section.size()).toBe(3); + } + + function test_load_dump_and_get_section_index () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.section = this.oktavia.getSection('document'); + + this.expect(this.section.getSectionIndex(0)).toBe(0); + this.expect(this.section.getSectionIndex(10)).toBe(0); + this.expect(this.section.getSectionIndex(11)).toBe(1); + this.expect(this.section.getSectionIndex(21)).toBe(1); + this.expect(this.section.getSectionIndex(22)).toBe(2); + this.expect(this.section.getSectionIndex(44)).toBe(2); + } + + function test_load_dump_and_get_section_index_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.section = this.oktavia.getSection('document'); + + try + { + this.section.getSectionIndex(-1); + this.fail("fm.getSectionIndex()"); + } + catch (e : Error) + { + } + try + { + this.section.getSectionIndex(45); + this.fail("fm.getSectionIndex()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_get_section_content () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.section = this.oktavia.getSection('document'); + + this.expect(this.section.getContent(0)).toBe("abracadabra"); + this.expect(this.section.getContent(1)).toBe("mississippi"); + this.expect(this.section.getContent(2)).toBe("abracadabra mississippi"); + } + + function test_load_dump_and_get_section_content_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.section = this.oktavia.getSection('document'); + + try + { + this.section.getContent(3); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.section.getContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_get_section_name () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.section = this.oktavia.getSection('document'); + + this.expect(this.section.getName(0)).toBe("doc1"); + this.expect(this.section.getName(1)).toBe("doc2"); + this.expect(this.section.getName(2)).toBe("doc3"); + } + + function test_load_dump_and_get_section_name_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.section = this.oktavia.getSection('document'); + + try + { + this.section.getName(3); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + try + { + this.section.getName(-1); + this.fail("fm.getName()"); + } + catch (e : Error) + { + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-splitter.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-splitter.jsx new file mode 100644 index 000000000..859eb9112 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-splitter.jsx @@ -0,0 +1,173 @@ +/** + * This is a JSX version of shellinford library: + * https://code.google.com/p/shellinford/ + * + * License: http://shibu.mit-license.org/ + */ + +import "test-case.jsx"; +import "oktavia.jsx"; +import "metadata.jsx"; + +class _Test extends TestCase +{ + var oktavia : Oktavia; + var splitter : Splitter; + + override function setUp () : void + { + this.oktavia = new Oktavia(); + this.splitter = this.oktavia.addSplitter('document'); + this.oktavia.addWord("abracadabra"); + this.splitter.split(); + this.oktavia.addWord("mississippi"); + this.splitter.split(); + this.oktavia.addWord("abracadabra mississippi"); + this.splitter.split(); + this.oktavia.build(25, false); + } + + function test_count () : void + { + this.expect(this.splitter.size()).toBe(3); + } + + function test_get_splitter_index () : void + { + this.expect(this.splitter.getIndex(0)).toBe(0); + this.expect(this.splitter.getIndex(10)).toBe(0); + this.expect(this.splitter.getIndex(11)).toBe(1); + this.expect(this.splitter.getIndex(21)).toBe(1); + this.expect(this.splitter.getIndex(22)).toBe(2); + this.expect(this.splitter.getIndex(44)).toBe(2); + } + + function test_get_splitter_index_boundary () : void + { + try + { + this.splitter.getIndex(-1); + this.fail("fm.getIndex()"); + } + catch (e : Error) + { + } + try + { + this.splitter.getIndex(45); + this.fail("fm.getIndex()"); + } + catch (e : Error) + { + } + } + + function test_get_splitter_content () : void + { + this.expect(this.splitter.getContent(2)).toBe("abracadabra mississippi"); + this.expect(this.splitter.getContent(1)).toBe("mississippi"); + this.expect(this.splitter.getContent(0)).toBe("abracadabra"); + } + + function test_get_splitter_content_boundary () : void + { + try + { + this.splitter.getContent(3); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.splitter.getContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_count () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.splitter = this.oktavia.getSplitter('document'); + + this.expect(this.splitter.size()).toBe(3); + } + + function test_load_dump_and_get_splitter_index () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.splitter = this.oktavia.getSplitter('document'); + + this.expect(this.splitter.getIndex(0)).toBe(0); + this.expect(this.splitter.getIndex(10)).toBe(0); + this.expect(this.splitter.getIndex(11)).toBe(1); + this.expect(this.splitter.getIndex(21)).toBe(1); + this.expect(this.splitter.getIndex(22)).toBe(2); + this.expect(this.splitter.getIndex(44)).toBe(2); + } + + function test_load_dump_and_get_splitter_index_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.splitter = this.oktavia.getSplitter('document'); + + try + { + this.splitter.getIndex(-1); + this.fail("fm.getIndex()"); + } + catch (e : Error) + { + } + try + { + this.splitter.getIndex(45); + this.fail("fm.getIndex()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_get_splitter_content () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.splitter = this.oktavia.getSplitter('document'); + + this.expect(this.splitter.getContent(2)).toBe("abracadabra mississippi"); + this.expect(this.splitter.getContent(1)).toBe("mississippi"); + this.expect(this.splitter.getContent(0)).toBe("abracadabra"); + } + + function test_load_dump_and_get_splitter_content_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.splitter = this.oktavia.getSplitter('document'); + + try + { + this.splitter.getContent(3); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.splitter.getContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-stemming.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-stemming.jsx new file mode 100644 index 000000000..d8d1d74eb --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-stemming.jsx @@ -0,0 +1,55 @@ +import "test-case.jsx"; +import "oktavia.jsx"; +import "metadata.jsx"; +import "stemmer/english-stemmer.jsx"; +import "console.jsx"; + +class _Test extends TestCase +{ + var oktavia : Oktavia; + var section : Section; + + override function setUp () : void + { + this.oktavia = new Oktavia(); + this.oktavia.setStemmer(new EnglishStemmer()); + this.section = this.oktavia.addSection('document'); + this.oktavia.addWord("stemming baby", true); + this.section.setTail("doc1"); + this.oktavia.addWord("stemmed babies", true); + this.section.setTail("doc2"); + this.oktavia.build(); + } + + function test_search_without_stemming () : void + { + var results = this.oktavia.rawSearch('baby', false); + this.expect(results.length).toBe(1); + } + + function test_search_with_stemming () : void + { + var results = this.oktavia.rawSearch('baby', true); + this.expect(results.length).toBe(1); + } + + function test_load_dump_and_search_without_stemming () : void + { + var dump = this.oktavia.dump(); + var oktavia = new Oktavia(); + oktavia.setStemmer(new EnglishStemmer()); + oktavia.load(dump); + var results = oktavia.rawSearch('baby', false); + this.expect(results.length).toBe(1); + } + + function test_load_dump_and_search_with_stemming () : void + { + var dump = this.oktavia.dump(); + var oktavia = new Oktavia(); + oktavia.setStemmer(new EnglishStemmer()); + oktavia.load(dump); + var results = oktavia.rawSearch('baby', true); + this.expect(results.length).toBe(1); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-table.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-table.jsx new file mode 100644 index 000000000..ee2aa0975 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-oktavia-table.jsx @@ -0,0 +1,213 @@ +import "test-case.jsx"; +import "oktavia.jsx"; +import "metadata.jsx"; + +class _Test extends TestCase +{ + var oktavia : Oktavia; + var table : Table; + + override function setUp () : void + { + this.oktavia = new Oktavia(); + this.table = this.oktavia.addTable('address book', ['zip', 'city', 'area code']); + + this.oktavia.addWord("94101"); // 5 + this.table.setColumnTail(); + this.oktavia.addWord("San Francisco"); // 13 + this.table.setColumnTail(); + this.oktavia.addWord("415"); // 3 + this.table.setColumnTail(); + this.table.setRowTail(); + + this.oktavia.addWord("94607"); // 5 + this.table.setColumnTail(); + this.oktavia.addWord("Oakland"); // 7 + this.table.setColumnTail(); + this.oktavia.addWord("510"); // 3 + this.table.setColumnTail(); + this.table.setRowTail(); + + this.oktavia.addWord("94401"); // 5 + this.table.setColumnTail(); + this.oktavia.addWord("San Mateo"); // 9 + this.table.setColumnTail(); + this.oktavia.addWord("650"); // 3 + this.table.setColumnTail(); + this.table.setRowTail(); + + this.oktavia.build(); + } + + function test_row_sizes () : void + { + this.expect(this.table.rowSize()).toBe(3); + } + + function test_column_sizes () : void + { + this.expect(this.table.columnSize()).toBe(3); + } + + function test_get_cell () : void + { + this.expect(this.table.getCell(0)[0]).toBe(0); + this.expect(this.table.getCell(0)[1]).toBe(0); + this.expect(this.table.getCell(22)[0]).toBe(0); + this.expect(this.table.getCell(22)[1]).toBe(2); + this.expect(this.table.getCell(24)[0]).toBe(1); + this.expect(this.table.getCell(24)[1]).toBe(0); + this.expect(this.table.getCell(40)[0]).toBe(1); + this.expect(this.table.getCell(40)[1]).toBe(2); + this.expect(this.table.getCell(42)[0]).toBe(2); + this.expect(this.table.getCell(42)[1]).toBe(0); + this.expect(this.table.getCell(60)[0]).toBe(2); + this.expect(this.table.getCell(60)[1]).toBe(2); + } + + function test_get_table_index_boundary () : void + { + try + { + this.table.getCell(-1); + this.fail("fm.gettableIndex()"); + } + catch (e : Error) + { + } + try + { + this.table.getCell(62); + this.fail("fm.gettableIndex()"); + } + catch (e : Error) + { + } + } + + function test_get_table_content () : void + { + var row = this.table.getRowContent(0); + this.expect(row['zip']).toBe('94101'); + this.expect(row['city']).toBe('San Francisco'); + this.expect(row['area code']).toBe('415'); + } + + function test_get_table_content_boundary () : void + { + try + { + this.table.getContent(3); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.table.getContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_row_sizes () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.table = this.oktavia.getTable('address book'); + + this.expect(this.table.rowSize()).toBe(3); + } + + function test_load_dump_and_column_sizes () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.table = this.oktavia.getTable('address book'); + + this.expect(this.table.columnSize()).toBe(3); + } + + function test_load_dump_and_get_cell () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.table = this.oktavia.getTable('address book'); + + this.expect(this.table.getCell(0)[0]).toBe(0); + this.expect(this.table.getCell(0)[1]).toBe(0); + this.expect(this.table.getCell(22)[0]).toBe(0); + this.expect(this.table.getCell(22)[1]).toBe(2); + this.expect(this.table.getCell(24)[0]).toBe(1); + this.expect(this.table.getCell(24)[1]).toBe(0); + this.expect(this.table.getCell(40)[0]).toBe(1); + this.expect(this.table.getCell(40)[1]).toBe(2); + this.expect(this.table.getCell(42)[0]).toBe(2); + this.expect(this.table.getCell(42)[1]).toBe(0); + this.expect(this.table.getCell(60)[0]).toBe(2); + this.expect(this.table.getCell(60)[1]).toBe(2); + } + + function test_load_dump_and_get_table_index_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.table = this.oktavia.getTable('address book'); + + try + { + this.table.getCell(-1); + this.fail("fm.gettableIndex()"); + } + catch (e : Error) + { + } + try + { + this.table.getCell(62); + this.fail("fm.gettableIndex()"); + } + catch (e : Error) + { + } + } + + function test_load_dump_and_get_table_content () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.table = this.oktavia.getTable('address book'); + + var row = this.table.getRowContent(0); + this.expect(row['zip']).toBe('94101'); + this.expect(row['city']).toBe('San Francisco'); + this.expect(row['area code']).toBe('415'); + } + + function test_load_dump_and_get_table_content_boundary () : void + { + var dump = this.oktavia.dump(); + this.oktavia.load(dump); + this.table = this.oktavia.getTable('address book'); + + try + { + this.table.getContent(3); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + try + { + this.table.getContent(-1); + this.fail("fm.getContent()"); + } + catch (e : Error) + { + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-query-parser.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-query-parser.jsx new file mode 100644 index 000000000..c04ac46e4 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-query-parser.jsx @@ -0,0 +1,92 @@ +import "test-case.jsx"; +import "query-parser.jsx"; + +class _Test extends TestCase +{ + var parser : QueryParser; + + override function setUp () : void + { + this.parser = new QueryParser(); + } + + function test_and () : void + { + this.parser.parse(['word1', 'word2']); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(false); + this.expect(this.parser.queries[1].raw).toBe(false); + } + + function test_or () : void + { + this.parser.parse(['word1', 'OR', 'word2']); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(true); + this.expect(this.parser.queries[1].not).toBe(false); + this.expect(this.parser.queries[1].raw).toBe(false); + } + + function test_not () : void + { + this.parser.parse(['word1', '-word2']); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(true); + this.expect(this.parser.queries[1].raw).toBe(false); + } + + function test_raw () : void + { + this.parser.parse(['word1', '"word2"']); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(false); + this.expect(this.parser.queries[1].raw).toBe(true); + } + + function test_raw_not () : void + { + this.parser.parse(['word1', '-"word2"']); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(true); + this.expect(this.parser.queries[1].raw).toBe(true); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-query-string-parser.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-query-string-parser.jsx new file mode 100644 index 000000000..94ec7c193 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-query-string-parser.jsx @@ -0,0 +1,94 @@ +import "test-case.jsx"; +import "query-string-parser.jsx"; + +class _Test extends TestCase +{ + var parser : QueryStringParser; + + override function setUp () : void + { + this.parser = new QueryStringParser(); + } + + function test_single_string_and () : void + { + this.parser.parse('word1 word2'); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(false); + this.expect(this.parser.queries[1].raw).toBe(false); + } + + function test_single_string_or () : void + { + this.parser.parse('word1 OR word2'); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(true); + this.expect(this.parser.queries[1].not).toBe(false); + this.expect(this.parser.queries[1].raw).toBe(false); + } + + function test_single_string_not () : void + { + this.parser.parse('word1 -word2'); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(true); + this.expect(this.parser.queries[1].raw).toBe(false); + } + + function test_single_string_raw () : void + { + this.parser.parse('word1 "word2"'); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(false); + this.expect(this.parser.queries[1].raw).toBe(true); + } + + function test_single_string_raw_not () : void + { + this.parser.parse('word1 -"word2"'); + this.expect(this.parser.queries.length).toBe(2); + + this.expect(this.parser.queries[0].word).toBe('word1'); + this.expect(this.parser.queries[0].or).toBe(false); + this.expect(this.parser.queries[0].not).toBe(false); + this.expect(this.parser.queries[0].raw).toBe(false); + + this.expect(this.parser.queries[1].word).toBe('word2'); + this.expect(this.parser.queries[1].or).toBe(false); + this.expect(this.parser.queries[1].not).toBe(true); + this.expect(this.parser.queries[1].raw).toBe(true); + } + +} + diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-sax.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-sax.jsx new file mode 100644 index 000000000..8310bf0a7 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-sax.jsx @@ -0,0 +1,147 @@ +import "sax.jsx"; +import "test-case.jsx"; + + +class TestHandler extends SAXHandler +{ + var events : string[]; + var param1 : string[]; + var param2 : string[]; + + function constructor (events : string[], param1 : string[], param2 : string[]) + { + this.events = events; + this.param1 = param1; + this.param2 = param2; + } + override function ontext (text : string) : void + { + this.events.push("ontext"); + this.param1.push(text); + this.param2.push(null); + } + override function ondoctype (doctype : string) : void + { + this.events.push("ondoctype"); + this.param1.push(doctype); + this.param2.push(null); + } + override function onopentag (tagname : string, attributes : Map.) : void + { + this.events.push("onopentag"); + this.param1.push(tagname); + this.param2.push(null); + } + override function onclosetag (tagname : string) : void + { + this.events.push("onclosetag"); + this.param1.push(tagname); + this.param2.push(null); + } + override function onattribute (name : string, value : string) : void + { + this.events.push("onattribute"); + this.param1.push(name); + this.param2.push(value); + } + override function oncomment (comment : string) : void + { + this.events.push("oncomment"); + this.param1.push(comment); + this.param2.push(null); + } + override function onend () : void + { + this.events.push("onend"); + this.param1.push(null); + this.param2.push(null); + } + override function onready () : void + { + this.events.push("onready"); + this.param1.push(null); + this.param2.push(null); + } + override function onscript (script : string) : void + { + this.events.push("onscript"); + this.param1.push(script); + this.param2.push(null); + } +} + +class _Test extends TestCase +{ + var handler : TestHandler; + var parser : SAXParser; + + var events : string[]; + var param1 : string[]; + var param2 : string[]; + + override function setUp () : void + { + this.events = [] : string[]; + this.param1 = [] : string[]; + this.param2 = [] : string[]; + this.handler = new TestHandler(this.events, this.param1, this.param2); + this.parser = new SAXParser(this.handler); + } + + function test_empty_input () : void + { + this.parser.parse(''); + this.expect(this.events[0]).toBe('onready'); + this.expect(this.events[1]).toBe('onend'); + } + + function test_doctype () : void + { + this.parser.parse(''); + this.expect(this.events[1]).toBe('ondoctype'); + this.expect(this.param1[1]).toBe('html'); + } + + function test_tag1 () : void + { + this.parser.parse(''); + this.expect(this.events[1]).toBe('onopentag'); + this.expect(this.param1[1]).toBe('html'); + this.expect(this.events[2]).toBe('onclosetag'); + this.expect(this.param1[2]).toBe('html'); + } + + function test_tag2 () : void + { + this.parser.parse(''); + this.expect(this.events[1]).toBe('onopentag'); + this.expect(this.param1[1]).toBe('html'); + this.expect(this.events[2]).toBe('onclosetag'); + this.expect(this.param1[2]).toBe('html'); + } + + function test_attribute () : void + { + this.parser.parse(''); + this.expect(this.events[1]).toBe('onattribute'); + this.expect(this.param1[1]).toBe('lang'); + this.expect(this.param2[1]).toBe('ja'); + this.expect(this.events[2]).toBe('onopentag'); + this.expect(this.param1[2]).toBe('html'); + } + + function test_text () : void + { + this.parser.parse('hello world'); + this.expect(this.events[3]).toBe('ontext'); + this.expect(this.param1[3]).toBe('hello world'); + } + + function test_comment () : void + { + this.parser.parse(''); + this.expect(this.events[3]).toBe('oncomment'); + this.expect(this.param1[3]).toBe('comment'); + } +} + diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-search-result.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-search-result.jsx new file mode 100644 index 000000000..81bcc05d0 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-search-result.jsx @@ -0,0 +1,159 @@ +import "test-case.jsx"; +import "search-result.jsx"; + + +class _Test extends TestCase +{ + function test_simple_registration () : void + { + var result = new SingleResult(); + var section = result.getSearchUnit(0); + section.addPosition('hello', 0, false); + section.addPosition('world', 7, false); + this.expect(section.size()).toBe(2); + } + + function test_duplicate_longer_word_is_kept () : void + { + var result = new SingleResult(); + var section = result.getSearchUnit(0); + section.addPosition('hello', 0, false); + section.addPosition('hello world', 0, false); + var position = section.get(0); + + this.expect(section.size()).toBe(1); + this.expect(position.word).toBe('hello world'); + } + + function test_duplicate_no_stemmed_word_is_kept () : void + { + var result = new SingleResult(); + var section = result.getSearchUnit(0); + section.addPosition('hello', 0, true); + section.addPosition('hello', 0, false); + var position = section.get(0); + + this.expect(section.size()).toBe(1); + this.expect(position.stemmed).toBe(false); + } + + function test_and_merge () : void + { + var result1 = new SingleResult(); + result1.getSearchUnit(0); + result1.getSearchUnit(1); + + var result2 = new SingleResult(); + result2.getSearchUnit(0); + + var result3 = result1.merge(result2); + + this.expect(result3.size()).toBe(1); + } + + function test_and_merge_2 () : void + { + var result1 = new SingleResult(); + result1.getSearchUnit(0); + result1.getSearchUnit(1); + + var result2 = new SingleResult(); + result2.getSearchUnit(2); + + var result3 = result1.merge(result2); + + this.expect(result3.size()).toBe(0); + } + + function test_or_merge () : void + { + var result1 = new SingleResult(); + result1.getSearchUnit(0); + result1.getSearchUnit(1); + + var result2 = new SingleResult(); + result2.getSearchUnit(0); + result2.getSearchUnit(2); + result2.or = true; + + var result3 = result1.merge(result2); + + this.expect(result3.size()).toBe(3); + } + + function test_not_merge () : void + { + var result1 = new SingleResult(); + result1.getSearchUnit(0); + result1.getSearchUnit(1); + result1.getSearchUnit(2); + + var result2 = new SingleResult(); + result2.getSearchUnit(0); + result2.getSearchUnit(2); + result2.not = true; + + var result3 = result1.merge(result2); + + this.expect(result3.size()).toBe(1); + } + + function test_merge () : void + { + var summary = new SearchSummary(); + var singleresult1 = new SingleResult(); + singleresult1.getSearchUnit(0); + singleresult1.getSearchUnit(1); + + var singleresult2 = new SingleResult(); + singleresult2.getSearchUnit(1); + + summary.add(singleresult1); + summary.add(singleresult2); + summary.mergeResult(); + + this.expect(summary.size()).toBe(1); + } + + function test_proposal () : void + { + var summary = new SearchSummary(); + var singleresult1 = new SingleResult(); + singleresult1.getSearchUnit(0); + singleresult1.getSearchUnit(1); + + var singleresult2 = new SingleResult(); + singleresult2.getSearchUnit(2); + + summary.add(singleresult1); + summary.add(singleresult2); + + var proposal = summary.getProposal(); + + this.expect(proposal[0].omit).toBe(1); + this.expect(proposal[0].expect).toBe(2); + this.expect(proposal[1].omit).toBe(0); + this.expect(proposal[1].expect).toBe(1); + } + + function test_sort () : void + { + var summary = new SearchSummary(); + var singleresult = new SingleResult(); + var section1 = singleresult.getSearchUnit(0); + var section2 = singleresult.getSearchUnit(1); + var section3 = singleresult.getSearchUnit(2); + + summary.add(singleresult); + summary.mergeResult(); + summary.result.getSearchUnit(0).score = 100; + summary.result.getSearchUnit(1).score = 300; + summary.result.getSearchUnit(2).score = 200; + + var result = summary.getSortedResult(); + this.expect(result.length).toBe(3); + this.expect(result[0].id).toBe(1); + this.expect(result[1].id).toBe(2); + this.expect(result[2].id).toBe(0); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/test/test-wavelet-matrix.jsx b/src/web/server/h2o/libh2o/misc/oktavia/test/test-wavelet-matrix.jsx new file mode 100644 index 000000000..049d18d89 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/test/test-wavelet-matrix.jsx @@ -0,0 +1,143 @@ +/** + * This is a JSX version of shellinford library: + * https://code.google.com/p/shellinford/ + * + * License: http://shibu.mit-license.org/ + */ + +import "test-case.jsx"; +import "wavelet-matrix.jsx"; + +class _Test extends TestCase +{ + var test_src : string; + var wm : WaveletMatrix; + var rd : int[][]; + var sd : int[][]; + var td : int[][]; + + override function setUp () : void + { + this.test_src = "abracadabra mississippi"; + this.wm = new WaveletMatrix(); + this.wm.build(this.test_src); + this.rd = [] : int[][]; + this.sd = [] : int[][]; + this.td = [] : int[][]; + + for (var i = 0; i < 256; i++) + { + this.rd.push([0] : int[]); + this.td.push([0] : int[]); + this.sd.push([] : int[]); + } + + for (var i = 0; i < this.test_src.length; i++) + { + for (var c = 0; c < 256; c++) + { + this.rd[c].push(this.rd[c][i]); + this.td[c].push(this.td[c][i]); + if (this.test_src.charCodeAt(i) == c) + { + this.rd[c][i + 1]++; + this.sd[c].push(i); + } + if (this.test_src.charCodeAt(i) < c) + { + this.td[c][i + 1]++; + } + } + } + } + + function test_size () : void + { + this.expect(this.wm.size()).toBe(this.test_src.length); + for (var c = 0; c < 256; c++) + { + this.expect(this.wm.size(c)).toBe(this.rd[c][this.wm.size()]); + } + } + + function test_get() : void + { + for (var i = 0; i < this.wm.size(); i++) + { + this.expect(this.wm.get(i)).toBe(this.test_src.charCodeAt((i))); + } + } + + function test_rank() : void + { + for (var c = 0; c < 256; c++) + { + for (var i = 0; i <= this.wm.size(); i++) + { + this.expect(this.wm.rank(i, c)).toBe(this.rd[c][i]); + } + } + } + + function test_rank_less_than() : void + { + for (var c = 0; c < 256; c++) + { + for (var i = 0; i <= this.wm.size(); i++) + { + this.expect(this.wm.rank_less_than(i, c)).toBe(this.td[c][i]); + } + } + } + + function test_load_dump_and_size () : void + { + var dump = this.wm.dump(); + this.wm.load(dump); + + this.expect(this.wm.size()).toBe(this.test_src.length); + for (var c = 0; c < 256; c++) + { + this.expect(this.wm.size(c)).toBe(this.rd[c][this.wm.size()]); + } + } + + function test_load_dump_and_get() : void + { + var dump = this.wm.dump(); + this.wm.load(dump); + + for (var i = 0; i < this.wm.size(); i++) + { + this.expect(this.wm.get(i)).toBe(this.test_src.charCodeAt((i))); + } + } + + function test_load_dump_and_rank() : void + { + var dump = this.wm.dump(); + this.wm.load(dump); + + for (var c = 0; c < 256; c++) + { + for (var i = 0; i <= this.wm.size(); i++) + { + this.expect(this.wm.rank(i, c)).toBe(this.rd[c][i]); + } + } + } + + function test_load_dump_and_rank_less_than() : void + { + var dump = this.wm.dump(); + this.wm.load(dump); + + for (var c = 0; c < 256; c++) + { + for (var i = 0; i <= this.wm.size(); i++) + { + this.expect(this.wm.rank_less_than(i, c)).toBe(this.td[c][i]); + } + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_literal.txt b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_literal.txt new file mode 100644 index 000000000..7415d7468 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_literal.txt @@ -0,0 +1,65 @@ +Literals + +Keywords + +The table lists the keyword literals of JSX. In contrary to JavaScript, there is no distinction between undefined and null. + +Table 1. List of Keyword Literals +Keyword Description +null [: type] declares null, may have the type annotated. The type is deducted (if possible) if the type annotation does not exist. +false a boolean constant +true a boolean constant +Number Literal + +Identical to JavaScript. + +String Literal + +Identical to JavaScript. + +RegExp Literal + +Identical to JavaScript. + +Function Literal + +Type annotations against arguments and return types are required for function declaration, unless the type can be deducted by the surrounding expression. + +// a function that takes no arguments, and returns void +function () : void {} + +// a function that takes one argument (of number), +// and returns a number that in incremented by one +function (n : number) : number { + return n + 1; +} + +// the argument types and return types may be omitted +// (if it is deductable from the outer expression) +var sum = 0; +[ 1, 2, 3 ].forEach(function (e) { + sum += e; +}); +log sum; // 6 + +// short-handed +var sum = 0; +[ 1, 2, 3 ].forEach((e) -> { sum += e; }); +log sum; // 6 + +// short-handed, single-statement function expression +var s = "a0b1c".replace(/[a-z]/g, (ch) -> ch.toUpperCase()); +log s; // A0B1C +A statement starting with function is parsed as inner function declaration, as is by JavaScript. Surround the function declaration with () if your intention is to create an anonymous function and call it immediately. + +// inner function declaration (when used within a function declaration) +function innerFunc() : void { + ...; +} + +// create an anonymous function and execute immediately +(function () : void { + ...; +})(); +See also: Member Function in Class, Interface, and Mixin. + diff --git a/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_operator.txt b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_operator.txt new file mode 100644 index 000000000..7b79f9324 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_operator.txt @@ -0,0 +1,68 @@ +Operators + +The operators of JSX are the same to those in JavaScript (ECMA-262 3rd edition) except for the following changes. + +types of the operands accepted by the operators are more restrictive +logical operators (&& ||) return boolean +binary ?: operator has been introduced (to cover the use of || in JavaScript to return non-boolean values) +introduction of the as operator +delete is a statement instead of an operator +The table below lists the operators supported by JSX. + +Table 1. List of Operators by Precedence +Operator Returned Type Operand Type(s) +(x)[1] typeof x +func(...) return type of the function +obj.prop typeof obj.prop obj: any object type +array[index] Nullable. array: Array. +index: number +map[key] Nullable. map: Map. +key: string +x++ +x-- typeof x number or int +obj instanceof type boolean obj: any object type +type: a Class, Interface, or Mixin +x as type[2] +x as __noconvert__ type[3] type +++x +--x typeof x number or int ++x +-x typeof x number or int +~x int number or int +! x boolean any +typeof x string variant +x * y +x % y number or int[4] number or int +x / y number number or int +x + y +x - y number or int[4] number or int +x + y string string +x << y +x >> y +x >>> y int number or int +x < y +x<= y +x > y +x >= y boolean number, int, string[5] +x in y boolean x: string +y: Map. +x == y +x != y boolean any except variant[5] +x & y int number or int +x ^ y int number or int +x | y int number or int +x && y boolean any +x || y boolean any +x ? y : z typeof y any[6] +x ?: y typeof x any[5] +x = y typeof x any[7] +x op[8]= y typeof x same as op +x, y typeof y any +grouping operator +cast operator +cast operator (without run-time type check) +int is returned if both operands are int +types of x and y should be equal, or either should be convertible to the other +types of y and z should be equal, or either should be convertible to the other +type of y should be convertible to type of x +any of: * / % + - << >> >>> & ^ | diff --git a/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_primitive_type.txt b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_primitive_type.txt new file mode 100644 index 000000000..c55778dc1 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_primitive_type.txt @@ -0,0 +1,68 @@ +Primitive Types + +JSX provides the following four primitive types. Primitive types are non-nullable. Int exists as a type, but would be equal to or slower than using number in some cases (the definition of int is: an integral number between -231 to 231-1, or NaN, or +-Infinity). + +boolean +number +string +(int) +Nullable Primitive Types + +A nullable counterpart exists for each primitive type. Values of the types are returned by [] operators of Array. and Map.. + +Nullable. +Nullable. +Nullable. +(Nullable.) +Variant Type + +A variant can hold any type of data (including null). To use the data, explicit cast to other data types is necessary. + +Built-in Object Types + +Object + +Object class is the root class for all objects. + +Array. + +The class represents an array, by providing the [] operator that takes a number as the argument, length property and other methods to manipulate the array. In contrast to JavaScript, the array is typed. An instance of Array. class may only store elements of type T or null. + +The size of the array automatically grows. null is returned when an element out of the current boundary is requested. + +There are two ways to create an array object; one is to use the new opreator, the other is to use the array initialiser. The type of the array returned by an array initialiser is deducted from the type of the elements. Type information should be annotated in cases where such deduction is impossible (such as when initializing an empty array). + +new Array.; // creates an empty array of numbers +new Array.(length); // creates an array of given length (elements are initialized to null) +[] : Array.; // creates an empty array of numbers +[ 1, 2, 3 ]; // creates an array of numbers with three elements: 1, 2, 3 +Map. + +The class represents an associative array (collection of key-values pairs), mapping strings to values of type T or null. + +Operator [] (that takes a string as the argument) is provided for registering / retreiving a keyed value. for..in statement can be used for iterating the keys. hasOwnProperty method is provided for checking whether or not a key-value pair of a particular name is registered. The delete statement can be used for unregistering a key-value pair. + +Map objects can be created in two ways; by using the new operator or by using the map initialiser. + +new Map.; // creates an empty map of strings to numbers +{} : Map.; // same as above +{ a: 1 }; // creates a map of strings to numbers that has one pair: ("a" => 1) +Boolean, Number, String + +Internal types used for applying methods against primitives. + +These types of objects are instantiated when applying the dot operator against the primitives. For exmaple, the following code snippet applies the operator against string "abc", that returns a String object wrapping the primitive value. Then the charAt method of the object is called and "a" (of type string) is returned. + +"abc".charAt(0) // returns "a" +Although being possible, it is discouraged to instantiate and store these values of the types (e.g. var s = new String("abc")). Use of the primitive types (or nullable primitive types) is preferable for performance and debugging reasons. + +Number and String classes also provide some useful class methods and constants, e.g. Number.parseInt(:string):number, String.encodeURIComponent(:string):string. + +JSX + +The class provides some methods for controlling the runtime environment. + +User-defined Types + +Users may define a new class by extending the Object class, or by declaring an interface or a mixin. See Class, Interface and Mixin. + diff --git a/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_tutorial.txt b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_tutorial.txt new file mode 100644 index 000000000..538238a39 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_tutorial.txt @@ -0,0 +1,213 @@ +Background + +JSX is a statically-typed, object-oriented programming language compiling to standalone JavaScript. The reason why JSX was developed is our need for a more robust programming language than JavaScript. However, JSX is fairly close to JavaScript especially in its statements and expressions. + +Statically-typed programming language is robust because certain sorts of problems, for example typos in variable names or missing function definitions, are detected at compile-time. This is important especially in middle- to large-scale software development in which a number of engineers may be engaged. + +Therefore, JSX is designed as a statically-typed language. All the values and variables have a static type and you can only assign a correctly-typed value to a variable. In addition, all the functions including closures have types which are determined by the types of parameters and the return values, where you cannot call a function with incorrectly typed arguments. + +Also, another important reason why JSX was developed is to boost JavaScript performance. JavaScript itself is not so slow but large-scale development tends to have many abstraction layers, e.g. proxy classes and accessor methods, which often have negative impact on performance. JSX boosts performance by inline expansion: function bodies are expanded to where they are being called, if the functions being called could be determined at compile-time. This is the power of the statically-typed language in terms of performance. + +Run "Hello, World!" + +Let's start by running our first JSX program: hello.jsx. We use the jsx command, which is the JSX compiler in the JSX distribution, to compile JSX source code to JavaScript. + +Type as follows in the JSX distribution and/or repository, and then you will see it saying "Hello, world!". + +$ bin/jsx --run example/hello.jsx +We will look into the hello.jsx source code in the next section. + +Program Structure + +Here is hello.jsx, the source code of the "Hello world!" example. You can see several features of JSX in this program, namely, static types and class structure within the source code. + +class _Main { + static function main(args : string[]) : void { + log "Hello, world!"; + } +} +Class _Main has a static member function (a.k.a. a class method) named main, that takes an array of strings and returns nothing. _Main.main(:string[]):void is the entry point of JSX applications that is called when a user invokes an application from command line. JSX, like Java, does not allow top-level statements or functions. + +The log statement is mapped to console.log() in JavaScript, which displays the arguments to stdout with a newline. + +Next, we look into another typical library class, Point: + +class Point { + var x = 0; + var y = 0; + + function constructor() { + } + + function constructor(x : number, y : number) { + this.set(x, y); + } + + function constructor(other : Point) { + this.set(other); + } + + function set(x : number, y : number) : void { + this.x = x; + this.y = y; + } + + function set(other : Point) : void { + this.x = other.x; + this.y = other.y; + } +} +As you can see, member variables of Point, var x and var y, are declared without types, but their types are deducted from their initial values to be number. + +You might be surprised at multiple definition of member functions: one takes no parameters and the others take parameters. They are overloaded by their types of parameters. When you construct the class with new Point(), the first constructor, which takes no parameters, is called. The second with two parameters will be called on new Point(2, 3) and the third with one parameter will be called as a copy constructor. Other forms of construction, e.g. new Point(42) or new Point("foo", "bar") will cause compilation errors of mismatching signatures. The Point#set() functions are also overloaded and the compiler know how to call the correct one. + +Static Types + +Basic type concept will be described in this section. Primitive types, object types, variant type, and Nullable types exist in JSX. + +Primitive types, e.g. string, boolean, or number are non-nullable, immutable types. + +var s : string = "hello"; +var n : number = 42; +var b : boolean = true; +Object types, e.g. string[] (array of string), functions or Date, are nullable, mutable types. + +var d : Date = new Date(); // Date +var f : function():void = function() : void { log "Hi!"; }; +var a : string[] = ["foo"]; // the same as Array.; +Variant type, which means "no static type information," is used for interacting with existing JavaScript APIs. Some JavaScript libraries may return a variant value, which type cannot be determined at compile time. All you can do on variant values is to check equality of a variant value to another variant value. You have to cast it to another type before doing anything else on the value. + +Nullable type is a meta type which indicates a value may be null. For example, the return type of Array.#shift() is Nullable.. When you use a Nullable value, you have to make sure of the value is not null. Only primitive types can be marked Nullable. Object types and variants are nullable by default. + +function shiftOrReturnEmptyString(args : string[]) : string { + if (args.length > 0) + return args.shift(); + else + return ""; +} +When the source code is compiled in debug mode (which is the default), the compiler will insert run-time type-checking code. An exception will be raised (or the debugger will be activated) when misuse of a null value as actual value is detected. Run-time type checks can be omitted by compiling the source code with the --release option. +Classes and Interfaces + +JSX is a class-based object-oriented language, and its class model is similar to Java. + +a class may extend another class (single inheritance) +a class may implement multiple interfaces +all classes share a single root class: the Object class +interface Flyable { + abstract function fly() : void; +} + +abstract class Animal { + function eat() : void { + log "An animal is eating!"; + } +} + +class Bat extends Animal implements Flyable { + override function fly() : void { + log "A bat is flying!"; + } +} + +abstract class Insect { +} + +class Bee extends Insect implements Flyable { + override function fly() : void { + log "A bee is flying!"; + } +} + +class _Main { + + static function main(args : string[]) : void { + // fo bar + var bat = new Bat(); + + var animal : Animal = bat; // OK. A bat is an animal. + animal.eat(); + + var flyable : Flyable = bat; // OK. A bat can fly + flyable.fly(); + + // for Bee + var bee = new Bee(); + + flyable = bee; // A bee is also flyable + flyable.fly(); + } +} +In the example, the Bat class extends the Animal class, so it inherits the Animal#eat() member function, and it can be assigned to a variable typed to Animal. The class also implements the Flyable interface overriding the Flyable#fly() member function, so it can be assigned to a variable typed Flyable. There's also another flyable class, Bee. By using the Flyable interface, it is possible to deal with both classes as a flyable being, even if the organ of a bee is completely different from that of a bat. + +When overriding a member function, the use the override keyword is mandatory. Otherwise the compiler will report an error. In other words, you are saved from unexpected interface changes in the base classes which cause compilation errors in derived classes instead of undesirable runtime errors. +Functions and Closures + +In JSX, functions are first-class objects and they have static types. You can declare a variable of a function type like var f : function(arg : number) : number, a function that takes a number as an argument and returns another number (or, just returns the same value as the argument; but it's not important here). The variable f can be called as f(42) from which you will get a number value. + +It is possible to define closures (or anonymous functions). They are typically used to implement event listeners, which are popular in GUI programming. Closures are similar to JavaScript except for what this points at: when a closure is defined within a member function, it refers to the receiver of the member function. See the following example. +class _Main { + var foo = 42; + + function constructor() { + var f = function() : void { + log this.foo; + }; + + f(); // says 42 + } + + static function main(args : string[]) : void { + var o = new _Main(); + } +} +Modules + +JSX has a module system. You can reuse JSX class libraries by the import statement. For example, the following program uses timer.jsx module, which exports the Timer class. + +import "timer.jsx"; + +class _Main { + + static function main(args : string[]) : void { + Timer.setTimeout(function() : void { + log "Hello, world!"; + }, 1000); + } + +} +A module may export multiple classes, but you can specify what modules you import or name a namespace which the module is imported into. + +Interface to Web Browsers + +The js/web.jsx module provides the interface to web browser APIs, e.g. the window object and DOM APIs. The example below shows how to insert a text node into an HTML. + +// hello.jsx +import "js/web.jsx"; + +class _Main { + + static function main(args : string[]) : void { + var document = dom.window.document; + + var text = document.createTextNode("Hello, world!"); + document.getElementById("hello").appendChild(text); + } + +} + + + + Hello, world! + + + +

+ + +Once you compile hello.jsx by the following command, then you can access the HTML and you will see it saying "Hello, world!." + +$ bin/jsx --executable web --output hello.jsx.js hello.jsx +Further Learning + +More documents can be found on the wiki. +If you are looking for examples, please refer to the examples on this web site, the example directory of the distribution, or to the links on Resouces page of the wiki. diff --git a/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_typeconversion.txt b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_typeconversion.txt new file mode 100644 index 000000000..ec138bb32 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/testdata/jsx_typeconversion.txt @@ -0,0 +1,40 @@ +Documents > Type Conversion +Type Conversion + +The as operator is used for: conversion between primitive types (including Nullable and variant), down-casting of object types. + +The conversion rules between primitive types are defined as follows. If the source type is a Nullable type and if the value is null, a run-time exception is raised under debug builds. The behavior is unspecified for release builds. + +The result of conversion from a variant type depends on the result of the typeof operator applied to the variant. + +Down-casting of an object type returns a reference to the casted object if successful, otherwise null. + +Table 1. Conversion between the Primitive Types using the As Operator +Source Type Destination Type Result +boolean number 0 if false, 1 if true +boolean int same as above +boolean string "false" if false, "true" if true +number boolean false if 0 or NaN, otherwise true +number int fractional part is removed, becomes 0 if NaN, may get rounded to between -231 and 231-1 +number string converted to string representation +int boolean false if 0, otherwise true +int number converted to number of same value +int string converted to string representation +string boolean false if the string is empty, otherwise true +string number 0 if the string is empty, a number if the string can be parsed as a string, otherwise NaN +string int equivalent to: as number as int +Table 2. Conversion from Variant using the As Operator +Result of typeof(variant) Destination Type Result +"undefined" boolean false +"undefined" number NaN +"undefined" int 0 +"undefined" string "undefined" +"null" boolean false +"null" number 0 +"null" int 0 +"null" string "null" +"boolean" any primitive type equivalent to the result of: boolean as type +"number" any primitive type equivalent to the result of: number as type +"string" any primitive type equivalent to the result of: string as type +"object" any primitive type depends on the actual type of the value +"object" any object type reference to the object if the value is an object of the specified type, otherwise null diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/httpstatus.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/httpstatus.jsx new file mode 100644 index 000000000..a4d7451e5 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/httpstatus.jsx @@ -0,0 +1,130 @@ +import "console.jsx"; +import "oktavia.jsx"; +import "metadata.jsx"; +import "query-parser.jsx"; +import "stemmer/english-stemmer.jsx"; + + +class HTTPStatus +{ + var oktavia : Oktavia; + var splitter : Splitter; + var httpstatus : string[]; + + function constructor () + { + this.oktavia = new Oktavia; + this.oktavia.setStemmer(new EnglishStemmer()); + this.splitter = this.oktavia.addSplitter('line break'); + this.makeIndex(); + } + + function makeIndex () : void + { + this.httpstatus = [ + "100: Continue", + "101: Switching Protocols", + "102: Processing", + "200: OK", + "201: Created", + "202: Accepted", + "203: Non-Authoritative Information", + "204: No Content", + "205: Reset Content", + "206: Partial Content", + "207: Multi-Status", + "208: Already Reported", + "300: Multiple Choices", + "301: Moved Permanently", + "302: Found", + "303: See Other", + "304: Not Modified", + "305: Use Proxy", + "307: Temporary Redirect", + "400: Bad Request", + "401: Unauthorized", + "402: Payment Required", + "403: Forbidden", + "404: Not Found", + "405: Method Not Allowed", + "406: Not Acceptable", + "407: Proxy Authentication Required", + "408: Request Timeout", + "409: Conflict", + "410: Gone", + "411: Length Required", + "412: Precondition Failed", + "413: Request Entity Too Large", + "414: Request-URI Too Large", + "415: Unsupported Media Type", + "416: Request Range Not Satisfiable", + "417: Expectation Failed", + "418: I'm a teapot", + "422: Unprocessable Entity", + "423: Locked", + "424: Failed Dependency", + "425: No code", + "426: Upgrade Required", + "428: Precondition Required", + "429: Too Many Requests", + "431: Request Header Fields Too Large", + "449: Retry with", + "500: Internal Server Error", + "501: Not Implemented", + "502: Bad Gateway", + "503: Service Unavailable", + "504: Gateway Timeout", + "505: HTTP Version Not Supported", + "506: Variant Also Negotiates", + "507: Insufficient Storage", + "509: Bandwidth Limit Exceeded", + "510: Not Extended" + ]; + for (var i in this.httpstatus) + { + this.oktavia.addWord(this.httpstatus[i], true); + this.splitter.split(); + } + this.oktavia.build(); + } + + function search (words : string[]) : string + { + var queryParser = new QueryParser(); + var queries = queryParser.parse(words); + if (queries.length == 0) + { + var result = this.httpstatus.join('\n'); + result = result + "\n\nToday's status: " + this.random(); + return result; + } + else + { + var summary = this.oktavia.search(queries); + if (summary.size() == 0) + { + return "not found "; + } + var resultWords = [] : string[]; + for (var i in summary.result.unitIds) + { + resultWords.push(this.splitter.getContent(summary.result.unitIds[i])); + } + return resultWords.join('\n'); + } + } + + function random () : string + { + return this.httpstatus[Math.round(Math.random() * this.httpstatus.length)]; + } +} + +class _Main +{ + static function main (argv : string []) : void + { + var httpstatus = new HTTPStatus(); + console.log(httpstatus.search(argv)); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-mkindex.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-mkindex.jsx new file mode 100644 index 000000000..f2593bc9e --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-mkindex.jsx @@ -0,0 +1,470 @@ +import "console.jsx"; +import "js/nodejs.jsx"; + +import "oktavia.jsx"; +import "getopt.jsx"; +import "htmlparser.jsx"; +import "csvparser.jsx"; +import "textparser.jsx"; +import "binary-util.jsx"; + +import "stemmer/stemmer.jsx"; +import "stemmer/danish-stemmer.jsx"; +import "stemmer/dutch-stemmer.jsx"; +import "stemmer/english-stemmer.jsx"; +import "stemmer/finnish-stemmer.jsx"; +import "stemmer/french-stemmer.jsx"; +import "stemmer/german-stemmer.jsx"; +import "stemmer/hungarian-stemmer.jsx"; +import "stemmer/italian-stemmer.jsx"; +import "stemmer/norwegian-stemmer.jsx"; +import "stemmer/porter-stemmer.jsx"; +import "stemmer/portuguese-stemmer.jsx"; +import "stemmer/romanian-stemmer.jsx"; +import "stemmer/russian-stemmer.jsx"; +import "stemmer/spanish-stemmer.jsx"; +import "stemmer/swedish-stemmer.jsx"; +import "stemmer/turkish-stemmer.jsx"; + + +class _Main +{ + static function usage () : void + { + console.log([ + "usage: oktavia_mkindex [options]", + "", + "Common Options:", + " -i, --input [input folder/file ] : Target files to search. .html, .csv, .txt are available.", + " -o, --output [outputfolder] : Directory that will store output files.", + " : This is a relative path from root.", + " : Default value is 'search'. ", + " -t, --type [type] : Export type. 'index'(default), 'base64', 'cmd', 'js',", + " : 'commonjs' are available.", + " : 'index' is a just index file. 'cmd' is a base64 code with search program.", + " : Others are base64 source code style output.", + " -m, --mode [mode] : Mode type. 'html', 'csv', 'text' are available.", + " -c, --cache-density [percent] : Cache data density. It effects file size and search speed.", + " : 100% become four times of base index file size. Default value is 5%.", + " : Valid value is 0.1% - 100%.", + " -n, --name [function] : A variable name for 'js' output or property name", + " : for 'js' and 'commonjs'. Default value is 'searchIndex'.", + " -q, --quiet : Hide detail information.", + " -h, --help : Display this message.", + "", + "HTML Mode Options:", + " -r, --root [document root] : Document root folder. Default is current. ", + " : Indexer creates result file path from this folder.", + " -p, --prefix [directory prefix] : Directory prefix for a document root from a server root.", + " : If your domain is example.com and 'manual' is passed,", + " : document root become http://example.com/manual/.", + " : It effects search result URL. Default value is '/'.", + " -u, --unit [search unit] : 'file', 'h1'-'h6'. Default value is 'file'.", + " -f, --filter [target tag] : Only contents inside this tag is indexed.", + " : Default value is \"article,#content,#main,div.body\".", + " -s, --stemmer [algorithm] : Select stemming algorithm.", + " -w, --word-splitter [splitter] : Use optional word splitter.", + " : 'ts' (TinySegmenter for Japanese) is available", + "", + "Text Mode Options:", + " -s, --stemmer [algorithm] : Select stemming algorithm.", + " -w, --word-splitter [splitter] : Use optional word splitter.", + " : 'ts' (TinySegmenter for Japanese) is available", + " -u, --unit [search unit] : file, block, line. Default value is 'file'.", + "", + "Supported Stemmer Algorithms:", + " danish, dutch, english, finnish, french german, hungarian italian", + " norwegian, porter, portuguese, romanian, russian, spanish, swedish, turkish" + ].join('\n')); + } + + static function main(args : string[]) : void + { + console.log("Search Engine Oktavia - Index Generator\n"); + + var inputs = [] : string[]; + var root = process.cwd(); + var prefix = '/'; + var output = "search"; + var showhelp = false; + var notrun = false; + var unit = 'file'; + var type = 'js'; + var mode = ''; + var verbose = true; + var filter = [] : string[]; + var algorithm : Nullable. = null; + var wordsplitter : Nullable. = null; + var cacheDensity : number = 5.0; + var name = null : Nullable.; + var validModes = ['html', 'csv', 'text']; + var validUnitsForHTML = ['file', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; + var validUnitsForText = ['file', 'block', 'line']; + var validStemmers = [ + 'danish', 'dutch', 'english', 'finnish', 'french', 'german', 'hungarian', + 'italian', 'norwegian', 'porter', 'portuguese', 'romanian', 'russian', + 'spanish', 'swedish', 'turkish' + ]; + var validTypes = ['index', 'base64', 'cmd', 'js', 'commonjs']; + var validWordSplitters = ['ts']; + + var optstring = "n:(name)q(quiet)m:(mode)i:(input)r:(root)p:(prefix)o:(output)h(help)u:(unit)f:(filter)s:(stemmer)w:(word-splitter)t:(type)c:(cache-density)"; + var parser = new BasicParser(optstring, args); + var opt = parser.getopt(); + while (opt) + { + switch (opt.option) + { + case "m": + if (validModes.indexOf(opt.optarg) == -1) + { + console.error("Option m/mode should be 'html', 'csv', 'text'."); + notrun = true; + } + mode = opt.optarg; + break; + case "i": + inputs.push(opt.optarg); + break; + case "r": + root = node.path.resolve(opt.optarg); + break; + case "p": + prefix = opt.optarg; + break; + case "n": + name = opt.optarg; + break; + case "o": + output = opt.optarg; + if (output.slice(0, 1) == '/') + { + output = output.slice(1); + } + break; + case "h": + showhelp = true; + break; + case "q": + verbose = false; + break; + case "u": + unit = opt.optarg; + break; + case "f": + var items = opt.optarg.split(','); + for (var i in items) + { + filter.push(items[i]); + } + break; + case "t": + if (validTypes.indexOf(opt.optarg) == -1) + { + console.error('Option -t/--type is invalid.'); + notrun = true; + } + else + { + type = opt.optarg; + } + break; + case "s": + if (validStemmers.indexOf(opt.optarg) == -1) + { + console.error('Option -s/--stemmer is invalid.'); + notrun = true; + } + else + { + algorithm = opt.optarg; + } + break; + case "w": + + break; + case "c": + var match = /(\d+\.?\d*)/.exec(opt.optarg); + if (match) + { + var tmpValue = match[1] as number; + if (0.1 <= tmpValue && tmpValue <= 100) + { + cacheDensity = tmpValue; + } + else + { + console.error('Option -c/--cache-density should be in 0.1 - 100.'); + notrun = true; + } + } + else + { + console.error('Option -c/--cache-density is invalid.'); + notrun = true; + } + break; + case "?": + notrun = true; + break; + } + opt = parser.getopt(); + } + var inputTextFiles = [] : string[]; + var inputHTMLFiles = [] : string[]; + var inputCSVFiles = [] : string[]; + if (filter.length == 0) + { + filter = ['article', '#content', '#main', 'div.body']; + } + for (var i in inputs) + { + var input = inputs[i]; + if (!node.fs.existsSync(input)) + { + console.error("Following input folder/file doesn't exist: " + input); + notrun = true; + } + else + { + var stat = node.fs.statSync(input); + if (stat.isFile()) + { + _Main._checkFileType(node.path.resolve(input), inputTextFiles, inputHTMLFiles, inputCSVFiles); + } + else if (stat.isDirectory()) + { + _Main._checkDirectory(input, inputTextFiles, inputHTMLFiles, inputCSVFiles); + } + else + { + console.error("Following input is not folder or file: " + input); + notrun = true; + } + } + } + if (inputTextFiles.length == 0 && inputHTMLFiles.length == 0 && inputCSVFiles.length == 0 || !mode) + { + showhelp = true; + } + if (showhelp) + { + _Main.usage(); + } + else if (!notrun) + { + var stemmer : Nullable. = null; + if (algorithm) + { + stemmer = _Main._createStemmer(algorithm); + } + var dump = null : Nullable.; + switch (mode) + { + case 'html': + var unitIndex = validUnitsForHTML.indexOf(unit); + if (unitIndex == -1) + { + console.error('Option -u/--unit should be file, h1, h2, h3, h4, h5, h6. But ' + unit); + } + else + { + var htmlParser = new HTMLParser(unitIndex, root, prefix, filter, stemmer); + for (var i = 0; i < inputHTMLFiles.length; i++) + { + htmlParser.parse(inputHTMLFiles[i]); + } + console.log('generating index...'); + if (verbose) + { + console.log(''); + } + dump = htmlParser.dump(cacheDensity, verbose); + } + break; + case 'csv': + var csvParser = new CSVParser(root, stemmer); + for (var i in inputCSVFiles) + { + csvParser.parse(inputCSVFiles[i]); + } + break; + case 'text': + if (validUnitsForText.indexOf(unit) == -1) + { + console.error('Option u/unit should be file, block, line. But ' + unit); + } + else + { + var textParser = new TextParser(unit, root, stemmer); + for (var i in inputTextFiles) + { + textParser.parse(inputTextFiles[i]); + } + } + break; + } + if (dump) + { + var indexFilePath = ""; + switch (type) + { + case 'index': + indexFilePath = node.path.resolve(root, output, 'searchindex.okt'); + var dirPath = node.path.dirname(indexFilePath); + _Main._mkdirP(dirPath); + node.fs.writeFileSync(indexFilePath, dump, "utf16le"); + break; + case 'base64': + indexFilePath = node.path.resolve(root, output, 'searchindex.okt.b64'); + var dirPath = node.path.dirname(indexFilePath); + _Main._mkdirP(dirPath); + node.fs.writeFileSync(indexFilePath, Binary.base64encode(dump), "utf8"); + break; + case 'cmd': + break; + case 'js': + indexFilePath = node.path.resolve(root, output, 'searchindex.js'); + var dirPath = node.path.dirname(indexFilePath); + _Main._mkdirP(dirPath); + if (name == null) + { + name = 'searchIndex'; + } + var contents = [ + '// Oktavia Search Index', + 'var ' + name + ' = "' + Binary.base64encode(dump) + '";', '' + ]; + node.fs.writeFileSync(indexFilePath, contents.join('\n'), "utf8"); + break; + case 'commonjs': + indexFilePath = node.path.resolve(root, output, 'searchindex.js'); + var dirPath = node.path.dirname(indexFilePath); + _Main._mkdirP(dirPath); + if (name == null) + { + name = 'searchIndex'; + } + var contents = [ + '// Oktavia Search Index', + 'exports.' + name + ' = "' + Binary.base64encode(dump) + '";', '' + ]; + node.fs.writeFileSync(indexFilePath, contents.join('\n'), "utf8"); + break; + } + if (indexFilePath) + { + console.log("generated: " + indexFilePath); + } + } + } + } + + static function _checkFileType (path : string, texts : string[], HTMLs : string[], CSVs : string[]) : void + { + var match = path.match(/(.*)\.(.*)/); + if (match && match[1]) + { + switch (match[2].toLowerCase()) + { + case 'html': + case 'htm': + HTMLs.push(path); + break; + case 'csv': + CSVs.push(path); + break; + default: + texts.push(path); + } + } + } + + static function _checkDirectory (path : string, texts : string[], HTMLs : string[], CSVs : string[]) : void + { + var files = node.fs.readdirSync(path); + for (var j in files) + { + var filepath = node.path.resolve(path, files[j]); + var stat = node.fs.statSync(filepath); + if (stat.isFile()) + { + _Main._checkFileType(filepath, texts, HTMLs, CSVs); + } + else if (stat.isDirectory()) + { + _Main._checkDirectory(filepath, texts, HTMLs, CSVs); + } + } + } + + static function _mkdirP (path : string) : void + { + if (node.fs.existsSync(path)) + { + return; + } + _Main._mkdirP(node.path.dirname(path)); + node.fs.mkdirSync(path); + } + + static function _createStemmer (algorithm : string) : Stemmer + { + var stemmer : Stemmer; + switch (algorithm.toLowerCase()) + { + case "danish": + stemmer = new DanishStemmer(); + break; + case "dutch": + stemmer = new DutchStemmer(); + break; + case "english": + stemmer = new EnglishStemmer(); + break; + case "finnish": + stemmer = new FinnishStemmer(); + break; + case "french": + stemmer = new FrenchStemmer(); + break; + case "german": + stemmer = new GermanStemmer(); + break; + case "hungarian": + stemmer = new HungarianStemmer(); + break; + case "italian": + stemmer = new ItalianStemmer(); + break; + case "norwegian": + stemmer = new NorwegianStemmer(); + break; + case "porter": + stemmer = new PorterStemmer(); + break; + case "portuguese": + stemmer = new PortugueseStemmer(); + break; + case "romanian": + stemmer = new RomanianStemmer(); + break; + case "russian": + stemmer = new RussianStemmer(); + break; + case "spanish": + stemmer = new SpanishStemmer(); + break; + case "swedish": + stemmer = new SwedishStemmer(); + break; + case "turkish": + stemmer = new TurkishStemmer(); + break; + default: + stemmer = new EnglishStemmer(); + break; + } + return stemmer; + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-search.jsx new file mode 100644 index 000000000..719c71b86 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/oktavia-search.jsx @@ -0,0 +1,370 @@ +import "console.jsx"; +import "js/nodejs.jsx"; + +import "oktavia.jsx"; +import "getopt.jsx"; +import "query-parser.jsx"; +import "search-result.jsx"; +import "style.jsx"; +import "binary-util.jsx"; + +import "stemmer/stemmer.jsx"; +import "stemmer/danish-stemmer.jsx"; +import "stemmer/dutch-stemmer.jsx"; +import "stemmer/english-stemmer.jsx"; +import "stemmer/finnish-stemmer.jsx"; +import "stemmer/french-stemmer.jsx"; +import "stemmer/german-stemmer.jsx"; +import "stemmer/hungarian-stemmer.jsx"; +import "stemmer/italian-stemmer.jsx"; +import "stemmer/norwegian-stemmer.jsx"; +import "stemmer/porter-stemmer.jsx"; +import "stemmer/portuguese-stemmer.jsx"; +import "stemmer/romanian-stemmer.jsx"; +import "stemmer/russian-stemmer.jsx"; +import "stemmer/spanish-stemmer.jsx"; +import "stemmer/swedish-stemmer.jsx"; +import "stemmer/turkish-stemmer.jsx"; + + +class Search +{ + var style : Style; + + function search (indexFile : string, queryStrings : string[], num : int, style : Style, algorithm : Nullable.) : void + { + this.style = style; + var oktavia = new Oktavia(); + if (algorithm != null) + { + oktavia.setStemmer(this.createStemmer(algorithm)); + } + if (!this.loadIndex(oktavia, indexFile)) + { + return; + } + console.time('searching'); + var queryParser = new QueryParser(); + queryParser.parse(queryStrings); + var summary = oktavia.search(queryParser.queries); + console.timeEnd('searching'); + if (summary.size() == 0) + { + this.notFound(summary, queryStrings); + } + else + { + this.showResult(oktavia, summary, num); + } + } + + function loadIndex (oktavia : Oktavia, filepath : string) : boolean + { + var ext = node.path.extname(filepath); + var content : string; + var result = true; + switch (ext) + { + case ".okt": + content = node.fs.readFileSync(filepath, "utf16le"); + oktavia.load(content); + break; + case ".b64": + content = node.fs.readFileSync(filepath, "utf8"); + oktavia.load(Binary.base64decode(content)); + break; + case ".js": + content = node.fs.readFileSync(filepath, "utf8"); + var index = content.indexOf('"'); + var lastIndex = content.lastIndexOf('"'); + oktavia.load(Binary.base64decode(content.slice(index, lastIndex))); + break; + default: + console.log("unknown file extension: " + ext); + result = false; + break; + } + return result; + } + + function sortResult (oktavia : Oktavia, summary : SearchSummary) : SearchUnit[] + { + for (var i = 0; i < summary.result.units.length; i++) + { + var score = 0; + var unit = summary.result.units[i]; + for (var pos in unit.positions) + { + var position = unit.positions[pos]; + if (oktavia.wordPositionType(position.position)) + { + score += 10; + } + else + { + score += 1; + } + if (!position.stemmed) + { + score += 2; + } + } + unit.score = score; + } + return summary.getSortedResult(); + } + + function showResult (oktavia : Oktavia, summary : SearchSummary, num : int) : void + { + var results = this.sortResult(oktavia, summary); + var style = this.style; + var metadata = oktavia.getPrimaryMetadata(); + for (var i = 0; i < results.length; i++) + { + var unit = results[i]; + var info = metadata.getInformation(unit.id).split(Oktavia.eob); + /*console.log(info.replace(Oktavia.eob, ' -- ') + '\n'); + + ' ----------------------------------------------- ' + + unit.score as string + ' pt');*/ + console.log(style.convert('' + info[0] + '') + ' ' + style.convert('' + info[1] + '')); + var offset = info[0].length + 1; + var content = metadata.getContent(unit.id); + var start = 0; + var positions = unit.getPositions(); + if (content.indexOf(info[0]) == 1) + { + content = content.slice(info[0].length + 2, content.length); + start += (info[0].length + 2); + } + var end = start + num; + var split = false; + if (positions[0].position > end - positions[0].word.length) + { + end = positions[0].position + Math.floor(num / 2); + split = true; + } + for (var j = positions.length - 1; j > -1; j--) + { + var pos = positions[j]; + if (pos.position + pos.word.length < end) + { + /*log('--------------begin : ' + (pos.position - start) as string); + log(content.slice(0, pos.position - start)); + log('--------------match : ' + pos.word.length as string); + .log(content.slice(pos.position - start, pos.position + pos.word.length - start)); + log('--------------match : ' + (content.length - pos.position + pos.word.length - start) as string); + log(content.slice(pos.position + pos.word.length - start, content.length)); + log('--------------end');*/ + content = [ + content.slice(0, pos.position - start), + style.convert('*').replace('*', content.slice(pos.position - start, pos.position + pos.word.length - start)), + content.slice(pos.position + pos.word.length - start, content.length) + ].join(''); + } + } + var text : string; + if (split) + { + text = [ + content.slice(0, Math.floor(num / 2)) + ' ...', + content.slice(-Math.floor(num / 2), end - start)].join('\n'); + } + else + { + text = content.slice(0, end - start) + ' ...\n'; + } + text = text.replace(Oktavia.eob, ' ').replace(/\n\n+/, '\n\n'); + console.log(text); + } + console.log(style.convert('' + (summary.size() as string) + " results.\n")); + } + + function notFound (summary : SearchSummary, query : string[]) : void + { + var style = this.style; + if (query.length > 1) + { + console.log("Suggestions:"); + var proposals = summary.getProposal(); + for (var i = 0; i < proposals.length; i++) + { + var proposal = proposals[i]; + var querywords = [] : string[]; + for (var j = 0; j < query.length; j++) + { + if (j != proposal.omit) + { + querywords.push(style.convert('' + query[j] + '')); + } + else + { + //querywords.push(style.convert('' + query[j] + '')); + } + } + console.log("* Expected result: " + querywords.join(" ") + " - " + (proposal.expect as string) + " hit"); + } + } + else + { + console.log(style.convert("Your search - " + query[0] + " - didn't match any documents.")); + } + } + + function createStemmer (algorithm : string) : Stemmer + { + var stemmer : Stemmer; + switch (algorithm.toLowerCase()) + { + case "danish": + stemmer = new DanishStemmer(); + break; + case "dutch": + stemmer = new DutchStemmer(); + break; + case "english": + stemmer = new EnglishStemmer(); + break; + case "finnish": + stemmer = new FinnishStemmer(); + break; + case "french": + stemmer = new FrenchStemmer(); + break; + case "german": + stemmer = new GermanStemmer(); + break; + case "hungarian": + stemmer = new HungarianStemmer(); + break; + case "italian": + stemmer = new ItalianStemmer(); + break; + case "norwegian": + stemmer = new NorwegianStemmer(); + break; + case "porter": + stemmer = new PorterStemmer(); + break; + case "portuguese": + stemmer = new PortugueseStemmer(); + break; + case "romanian": + stemmer = new RomanianStemmer(); + break; + case "russian": + stemmer = new RussianStemmer(); + break; + case "spanish": + stemmer = new SpanishStemmer(); + break; + case "swedish": + stemmer = new SwedishStemmer(); + break; + case "turkish": + stemmer = new TurkishStemmer(); + break; + default: + stemmer = new EnglishStemmer(); + break; + } + return stemmer; + } +} + +class _Main { + static function usage () : void + { + console.log([ + "usage: oktavia_search index_file [options] query...", + "", + "Options:", + " -m, --mono : Don't use color.", + " -s, --stemmer [algorithm] : Select stemming algorithm.", + " -n, --number [char number] : Result display number. Default value = 250", + " -h, --help : Display this message.", + "", + "Search Query Syntax:", + " word1 word2 : All words.", + ' "word1 word2" : Exact words or phrase.', + " word1 OR word2 : Any of these words.", + " word1 -word2 : None of these words." + ].join('\n')); + } + + static function main(args : string[]) : void + { + console.log("Search Engine Oktavia - Command-line Search Client\n"); + + var indexFile : Nullable. = null; + var showhelp = false; + var notrun = false; + var styleType = 'console'; + var num : int = 250; + var queryStrings = [] : string[]; + var algorithm : Nullable. = null; + + var validStemmers = [ + 'danish', 'dutch', 'english', 'finnish', 'french', 'german', 'hungarian', + 'italian', 'norwegian', 'porter', 'portuguese', 'romanian', 'russian', + 'spanish', 'swedish', 'turkish' + ]; + + if (args.length == 0) + { + showhelp = true; + } + else if (!node.fs.existsSync(args[0])) + { + console.error("Index file '" + args[0] + "' doesn't exist."); + notrun = true; + } + else + { + indexFile = args[0]; + } + + var optstring = "m(mono)s:(stemmer)n:(number)h(help)"; + var parser = new BasicParser(optstring, args.slice(1)); + var opt = parser.getopt(); + while (opt) + { + switch (opt.option) + { + case "s": + if (validStemmers.indexOf(opt.optarg) == -1) + { + console.error('Option s/stemmer is invalid.'); + notrun = true; + } + else + { + algorithm = opt.optarg; + } + break; + case "m": + styleType = 'ignore'; + break; + case "n": + num = opt.optarg as int; + break; + case "h": + showhelp = true; + break; + default: + queryStrings.push(opt.option); + break; + } + opt = parser.getopt(); + } + if (showhelp || queryStrings.length == 0) + { + _Main.usage(); + } + else if (!notrun) + { + var style = new Style(styleType); + var search = new Search(); + search.search(indexFile, queryStrings, num, style, algorithm); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/search_simple.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/search_simple.jsx new file mode 100644 index 000000000..f9b867511 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/search_simple.jsx @@ -0,0 +1,39 @@ +import "nodejs.jsx"; +import "fm_index.jsx"; + +class _Main +{ + static function usage () : void + { + log "Simple FM-Index Search Engine: Oktavia"; + log ""; + log "[usage]"; + log " search [input db file name] keyword"; + } + + static function main(args : string[]) : void + { + if (args.length <2) + { + _Main.usage(); + } + else + { + var indexFileName = args.shift(); + log "index file name: ", indexFileName; + var fm_index = new FMIndex(); + fm_index.load(node.fs.readFileSync(indexFileName, "utf16le")); + for (var i in args) + { + log "[search world]", args[i]; + var results = fm_index.search(args[i]); + for (var j in results) + { + var result = results[j]; + log "[", result[0], "]: ", "(", result[1], ")"; + } + log results.length, " hits"; + } + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-danish-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-danish-search.jsx new file mode 100644 index 000000000..98dba01dd --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-danish-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/danish-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new DanishStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-dutch-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-dutch-search.jsx new file mode 100644 index 000000000..117f2cce4 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-dutch-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/dutch-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new DutchStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-english-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-english-search.jsx new file mode 100644 index 000000000..d30ad2ccf --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-english-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/english-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new EnglishStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-finnish-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-finnish-search.jsx new file mode 100644 index 000000000..640063958 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-finnish-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/finnish-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new FinnishStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-french-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-french-search.jsx new file mode 100644 index 000000000..777f5e2a3 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-french-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/french-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new FrenchStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-german-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-german-search.jsx new file mode 100644 index 000000000..588318704 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-german-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/german-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new GermanStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-hungarian-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-hungarian-search.jsx new file mode 100644 index 000000000..a14fe3450 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-hungarian-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/hungarian-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new HungarianStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-italian-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-italian-search.jsx new file mode 100644 index 000000000..30769d1a6 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-italian-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/italian-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new ItalianStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-norwegian-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-norwegian-search.jsx new file mode 100644 index 000000000..180e6b045 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-norwegian-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/norwegian-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new NorwegianStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-porter-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-porter-search.jsx new file mode 100644 index 000000000..ba1de086e --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-porter-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/porter-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new PorterStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-portuguese-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-portuguese-search.jsx new file mode 100644 index 000000000..89ed1a0f8 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-portuguese-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/portuguese-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new PortugueseStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-romanian-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-romanian-search.jsx new file mode 100644 index 000000000..ef8b47fca --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-romanian-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/romanian-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new RomanianStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-russian-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-russian-search.jsx new file mode 100644 index 000000000..2a572d712 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-russian-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/russian-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new RussianStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-search.jsx new file mode 100644 index 000000000..22dc3f779 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-search.jsx @@ -0,0 +1,327 @@ +import "oktavia.jsx"; +import "binary-util.jsx"; +import "query.jsx"; +import "query-string-parser.jsx"; +import "search-result.jsx"; +import "style.jsx"; +import "stemmer/stemmer.jsx"; +import "console.jsx"; + + +class _Result +{ + var title : string; + var url : string; + var content : string; + var score : int; + function constructor (title : string, url : string, content : string, score : int) + { + this.title = title; + this.url = url; + this.content = content; + this.score = score; + } +} + +class _Proposal +{ + var options : string; + var label : string; + var count : int; + function constructor (options : string, label : string, count : int) + { + this.options = options; + this.label = label; + this.count = count; + } +} + +class OktaviaSearch +{ + var _oktavia : Oktavia; + static var _stemmer : Nullable. = null; + static var _instance : Nullable. = null; + var _queryString : Nullable.; + var _queries : Query[]; + var _highlight : string; + var _callback : Nullable.; + var _entriesPerPage : int; + var _currentPage : int; + var _result : SearchUnit[]; + var _proposals : Proposal[]; + var _currentFolderDepth : int; + + function constructor (entriesPerPage : int) + { + this._oktavia = new Oktavia(); + this._entriesPerPage = entriesPerPage; + this._currentPage = 1; + this._queryString = null; + this._callback = null; + OktaviaSearch._instance = this; + } + + static function setStemmer(stemmer : Stemmer) : void + { + if (OktaviaSearch._instance) + { + OktaviaSearch._instance._oktavia.setStemmer(stemmer); + } + else + { + OktaviaSearch._stemmer = stemmer; + } + } + + function loadIndex (index : string) : void + { + if (OktaviaSearch._stemmer) + { + this._oktavia.setStemmer(OktaviaSearch._stemmer); + } + this._oktavia.load(Binary.base64decode(index)); + if (this._queryString) + { + this.search(this._queryString, this._callback); + this._queryString = null; + this._callback = null; + } + } + + function search (queryString : string, callback : function(:int, :int):void) : void + { + if (this._oktavia) + { + var queryParser = new QueryStringParser(); + this._queries = queryParser.parse(queryString); + this._highlight = queryParser.highlight(); + var summary = this._oktavia.search(this._queries); + if (summary.size() > 0) + { + this._result = this._sortResult(summary); + this._proposals = [] : Proposal[]; + this._currentPage = 1; + } + else + { + this._result = [] : SearchUnit[]; + if (this._queries.length > 1) + { + this._proposals = summary.getProposal(); + } + else + { + this._proposals = [] : Proposal[]; + } + this._currentPage = 1; + } + callback(this.resultSize(), this.totalPages()); + } + else + { + this._queryString = queryString; + this._callback = callback; + } + } + + function resultSize () : int + { + return this._result.length; + } + + function totalPages () : int + { + return Math.ceil(this._result.length / this._entriesPerPage); + } + + function currentPage () : int + { + return this._currentPage; + } + + function setCurrentPage (page : int) : void + { + this._currentPage = page; + } + + function hasPrevPage () : boolean + { + return (this._currentPage != 1); + } + + function hasNextPage () : boolean + { + return (this._currentPage != this.totalPages()); + } + + function pageIndexes () : string[] + { + var result = [] : string[]; + var total = this.totalPages(); + if (total < 10) + { + for (var i = 1; i <= total; i++) + { + result.push(i as string); + } + } + else if (this._currentPage <= 5) + { + for (var i = 1; i <= 7; i++) + { + result.push(i as string); + } + result.push('...', total as string); + } + else if (total - 5 <= this._currentPage) + { + result.push('1', '...'); + for (var i = total - 8; i <= total; i++) + { + result.push(i as string); + } + } + else + { + result.push('1', '...'); + for (var i = this._currentPage - 3; i <= this._currentPage + 3; i++) + { + result.push(i as string); + } + result.push('...', total as string); + } + return result; + } + + function getResult () : _Result[] + { + var style = new Style('html'); + var start = (this._currentPage - 1) * this._entriesPerPage; + var last = Math.min(this._currentPage * this._entriesPerPage, this._result.length); + var metadata = this._oktavia.getPrimaryMetadata(); + var num = 250; + + var results = [] : _Result[]; + + for (var i = start; i < last; i++) + { + var unit = this._result[i]; + var info = metadata.getInformation(unit.id).split(Oktavia.eob); + + var offset = info[0].length + 1; + var content = metadata.getContent(unit.id); + var start = 0; + var positions = unit.getPositions(); + if (content.indexOf(info[0]) == 1) + { + content = content.slice(info[0].length + 2, content.length); + start += (info[0].length + 2); + } + var end = start + num; + var split = false; + if (positions[0].position > end - positions[0].word.length) + { + end = positions[0].position + Math.floor(num / 2); + split = true; + } + for (var j = positions.length - 1; j > -1; j--) + { + var pos = positions[j]; + if (pos.position + pos.word.length < end) + { + content = [ + content.slice(0, pos.position - start), + style.convert('*').replace('*', content.slice(pos.position - start, pos.position + pos.word.length - start)), + content.slice(pos.position + pos.word.length - start, content.length) + ].join(''); + } + } + var text : string; + if (split) + { + text = [ + content.slice(0, Math.floor(num / 2)) + ' ...', + content.slice(-Math.floor(num / 2), end - start)].join('
'); + } + else + { + text = content.slice(0, end - start) + ' ...
'; + } + text = text.replace(Oktavia.eob, ' ').replace(/()()+/, '

'); + results.push(new _Result(info[0], info[1], text, unit.score)); + } + return results; + } + + function getHighlight () : string + { + return this._highlight; + } + + function getProposals () : _Proposal[] + { + var style = new Style('html'); + var results = [] : _Proposal[]; + + if (this._queries.length > 1) + { + for (var i = 0; i < this._proposals.length; i++) + { + var proposal = this._proposals[i]; + if (proposal.expect > 0) + { + var label = [] : string[]; + var option = [] : string[]; + for (var j = 0; j < this._queries.length; j++) + { + if (j != proposal.omit) + { + label.push(style.convert('' + this._queries[j].toString() + '')); + option.push(this._queries[j].toString()); + } + else + { + label.push(style.convert('' + this._queries[j].toString() + '')); + } + } + results.push(new _Proposal(option.join(' '), label.join(' '), proposal.expect)); + } + } + } + return results; + } + + function _sortResult (summary : SearchSummary) : SearchUnit[] + { + for (var i = 0; i < summary.result.units.length; i++) + { + var score = 0; + var unit = summary.result.units[i]; + for (var pos in unit.positions) + { + var position = unit.positions[pos]; + if (this._oktavia.wordPositionType(position.position)) + { + score += 10; + } + else + { + score += 1; + } + if (!position.stemmed) + { + score += 2; + } + } + unit.score = score; + } + return summary.getSortedResult(); + } +} + +class _Main +{ + static function main(args : string[]) : void + { + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-spanish-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-spanish-search.jsx new file mode 100644 index 000000000..3ad9b8d1e --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-spanish-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/spanish-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new SpanishStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-swedish-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-swedish-search.jsx new file mode 100644 index 000000000..9b900a484 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-swedish-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/swedish-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new SwedishStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-turkish-search.jsx b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-turkish-search.jsx new file mode 100644 index 000000000..24d040584 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/oktavia/tool/web/oktavia-turkish-search.jsx @@ -0,0 +1,10 @@ +import "oktavia-search.jsx"; +import "stemmer/turkish-stemmer.jsx"; + +class _Main +{ + static function main(args : string[]) : void + { + OktaviaSearch.setStemmer(new TurkishStemmer); + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/.gitignore b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/.gitignore new file mode 100644 index 000000000..b76c8adfc --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/.gitignore @@ -0,0 +1,32 @@ +Makefile +inc/ +.c +ppport.h +.DS_Store +.*.sw[pon] +*.bak +*.old +Build +_build/ +xshelper.h +META.yml +MYMETA.yml +MYMETA.json +MANIFEST +MANIFEST.bak +.online +blib/ +pm_to_blib +nytprof* +core +perltidy.ERR +tmp/ +Server-Starter-*.tar.gz +/Server-Starter-* +/.build +/_build_params +/Build +/Build.bat +!Build/ +!META.json +!LICENSE diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/.travis.yml b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/.travis.yml new file mode 100644 index 000000000..f3168e947 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/.travis.yml @@ -0,0 +1,8 @@ +language: perl +sudo: false +perl: + - "5.12" + - "5.14" + - "5.16" + - "5.18" + - "5.20" diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/Build.PL b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/Build.PL new file mode 100644 index 000000000..ffc64f5af --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/Build.PL @@ -0,0 +1,65 @@ +# ========================================================================= +# THIS FILE IS AUTOMATICALLY GENERATED BY MINILLA. +# DO NOT EDIT DIRECTLY. +# ========================================================================= + +use 5.008_001; + +use strict; +use warnings; +use utf8; + +use builder::MyBuilder; +use File::Basename; +use File::Spec; + +my %args = ( + license => 'perl', + dynamic_config => 0, + + configure_requires => { + 'Module::Build' => 0.38, + }, + + name => 'Server-Starter', + module_name => 'Server::Starter', + allow_pureperl => 0, + + script_files => [glob('script/*'), glob('bin/*')], + c_source => [qw()], + PL_files => {}, + + test_files => ((-d '.git' || $ENV{RELEASE_TESTING}) && -d 'xt') ? 't/ xt/' : 't/', + recursive_test_files => 1, + + +); +if (-d 'share') { + $args{share_dir} = 'share'; +} + +my $builder = builder::MyBuilder->subclass( + class => 'MyBuilder', + code => q{ + sub ACTION_distmeta { + die "Do not run distmeta. Install Minilla and `minil install` instead.\n"; + } + sub ACTION_installdeps { + die "Do not run installdeps. Run `cpanm --installdeps .` instead.\n"; + } + } +)->new(%args); +$builder->create_build_script(); + +use File::Copy; + +print "cp META.json MYMETA.json\n"; +copy("META.json","MYMETA.json") or die "Copy failed(META.json): $!"; + +if (-f 'META.yml') { + print "cp META.yml MYMETA.yml\n"; + copy("META.yml","MYMETA.yml") or die "Copy failed(META.yml): $!"; +} else { + print "There is no META.yml... You may install this module from the repository...\n"; +} + diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/Changes b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/Changes new file mode 100644 index 000000000..1232b8649 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/Changes @@ -0,0 +1,104 @@ +Revision history for Perl extension Server::Starter. + +{{$NEXT}} + +0.31 2015-07-20T02:38:57Z + - do not remove the socket file when becoming a daemon (thanks to andyjones) #34 #36 + - emit name of the directory to which it failed to chdir(2) (thanks to tokuhirom) #33 + +0.30 2015-06-05T05:28:43Z + - unlink the status file only when created by itself (thanks to tokuhirom) #32 + - redo #26 (thanks to tokuhirom) #31 + +0.29 2015-06-04T06:45:26Z + - build should fail on Windows (thanks to chorny) #26 + - add `--stop` option (thanks to tokuhirom) #28 + - do not close STDIN in case the listening port is mapped to fd zero (thanks to tokuhirom) #29 #24 + - reopen STDIN to suppress unnecessary warnings (thanks to touhirom) #30 + +0.28 2015-05-28T22:08:37Z + - add `--port=[host:]port=fd` option for specifying the file descriptor number (thanks to tokuhirom) #24 + +0.27 2015-04-28T01:02:28Z + - revert 0.26 so that the install script can update the shebang (thanks to miyagawa) #22 + - modernize the build tool (thanks to miyagawa) #23 + +0.26 + - `start_server` command uses perl found in $PATH instead of /usr/bin/perl #21 + +0.25 + - fix `already in use` error if the program is restarted (regression in 0.21) #18 + - tests now pass on environments wo. IPv6 support #19 + +0.24 + - introduce --daemonize option (#18 #6) + - fix bug that causes a infinite loop in shutdown (amends #14) + +0.23 + - set IPV6_V6ONLY for socket bound to an IPv6 address (#16) + +0.22 + - support for IPv6 (#16) + - include repository URL in META.yml (#15; thanks to ether) + +0.21 + - remove dependency against non-standard modules (#14) + +0.19 + - reimplement changes in 0.15, 0.16 for stability (#13) + - update inc/Module/Install + +0.17 + - add option: --backlog to change the backlog size (default: SOMAXCONN) (thanks to Yuryu) + +0.16 + - [bugfix] unset the environment variable when a file is removed from the directory specified by --envdir + +0.15 + - added option: --envdir for reloading configuration (thanks to limitusus) + - added options: --enable-auto-restart and interval for periodical automatic restarting (thanks to limitusus) + - added option: --kill-old-delay for delaying SIGTERM (thanks to limitusus) + +0.14 + - fix regression in 0.13; start_server wo. "--dir" was causing errors + +0.13 + - add option: --dir (thanks to kazeburo) + +0.12 + - bugfix: support for programs with whitespaces (thanks to clkao) + - add option: --signal-on-term (thanks to miyagawa) + +0.11 + - remove unix socket file on shutdown + +0.10 + - support for unix sockets with --path option + +0.09 + - added options: --signal-on-hup, --status-file, --restart + +0.08 + - added --pid-file option + +0.07 Sat May 08 14:00:00 2010 + - --port option is now omittable (so daemons _not_ binding to TCP ports (like FCGI servers binding to unix domain sockets) can be hot-deployied using Server::Starter) + +0.06 Sat Jan 02 10:26:00 2010 + - bugfix: start_server did not shutdown upon receiving SIGINT while the worker is failing to start up + +0.05 Tue Oct 13 20:30:00 2009 + - use $^X in tests (or tests will fail on hosts using a different installation of perl from one installed to /usr/bin/perl) + +0.04 Fri Oct 09 19:25:00 2009 + - signals received just after spawning worker process were dismissed (thanks to kazeburo for reporting and helping fix the problem) + +0.03 Thu Sep 24 20:00:00 2009 + - require 5.008 + - require Test::TCP 0.11 to satisfy the dependencies + +0.02 Thu Sep 09 17:06:00 2009 + - add README, Changes + +0.01 Thu Sep 09 17:00:00 2009 + - initial version diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/LICENSE b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/LICENSE new file mode 100644 index 000000000..a4d95a593 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/LICENSE @@ -0,0 +1,379 @@ +This software is copyright (c) 2015 by Kazuho Oku. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +Terms of the Perl programming language system itself + +a) the GNU General Public License as published by the Free + Software Foundation; either version 1, or (at your option) any + later version, or +b) the "Artistic License" + +--- The GNU General Public License, Version 1, February 1989 --- + +This software is Copyright (c) 2015 by Kazuho Oku. + +This is free software, licensed under: + + The GNU General Public License, Version 1, February 1989 + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! + + +--- The Artistic License 1.0 --- + +This software is Copyright (c) 2015 by Kazuho Oku. + +This is free software, licensed under: + + The Artistic License 1.0 + +The Artistic License + +Preamble + +The intent of this document is to state the conditions under which a Package +may be copied, such that the Copyright Holder maintains some semblance of +artistic control over the development of the package, while giving the users of +the package the right to use and distribute the Package in a more-or-less +customary fashion, plus the right to make reasonable modifications. + +Definitions: + + - "Package" refers to the collection of files distributed by the Copyright + Holder, and derivatives of that collection of files created through + textual modification. + - "Standard Version" refers to such a Package if it has not been modified, + or has been modified in accordance with the wishes of the Copyright + Holder. + - "Copyright Holder" is whoever is named in the copyright or copyrights for + the package. + - "You" is you, if you're thinking about copying or distributing this Package. + - "Reasonable copying fee" is whatever you can justify on the basis of media + cost, duplication charges, time of people involved, and so on. (You will + not be required to justify it to the Copyright Holder, but only to the + computing community at large as a market that must bear the fee.) + - "Freely Available" means that no fee is charged for the item itself, though + there may be fees involved in handling the item. It also means that + recipients of the item may redistribute it under the same conditions they + received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications derived +from the Public Domain or from the Copyright Holder. A Package modified in such +a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided that +you insert a prominent notice in each changed file stating how and when you +changed that file, and provided that you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or an + equivalent medium, or placing the modifications on a major archive site + such as ftp.uu.net, or by allowing the Copyright Holder to include your + modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict with + standard executables, which must also be provided, and provide a separate + manual page for each non-standard executable that clearly documents how it + differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or executable +form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where to + get the Standard Version. + + b) accompany the distribution with the machine-readable source of the Package + with your modifications. + + c) accompany any non-standard executables with their corresponding Standard + Version executables, giving the non-standard executables non-standard + names, and clearly documenting the differences in manual pages (or + equivalent), together with instructions on where to get the Standard + Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this Package. You +may not charge a fee for this Package itself. However, you may distribute this +Package in aggregate with other (possibly commercial) programs as part of a +larger (possibly commercial) software distribution provided that you do not +advertise this Package as a product of your own. + +6. The scripts and library files supplied as input to or produced as output +from the programs of this Package do not automatically fall under the copyright +of this Package, but belong to whomever generated them, and may be sold +commercially, and may be aggregated with this Package. + +7. C or perl subroutines supplied by you and linked into this Package shall not +be considered part of this Package. + +8. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +The End + diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/META.json b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/META.json new file mode 100644 index 000000000..64f85645b --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/META.json @@ -0,0 +1,89 @@ +{ + "abstract" : "a superdaemon for hot-deploying server programs", + "author" : [ + "Kazuho Oku" + ], + "dynamic_config" : 0, + "generated_by" : "Minilla/v2.3.0, CPAN::Meta::Converter version 2.150001", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "Server-Starter", + "no_index" : { + "directory" : [ + "t", + "xt", + "inc", + "share", + "eg", + "examples", + "author", + "builder" + ] + }, + "prereqs" : { + "configure" : { + "requires" : { + "Module::Build" : "0.38" + } + }, + "develop" : { + "requires" : { + "Test::CPAN::Meta" : "0", + "Test::MinimumVersion::Fast" : "0.04", + "Test::PAUSE::Permissions" : "0.04", + "Test::Pod" : "1.41", + "Test::Spellunker" : "v0.2.7" + } + }, + "runtime" : { + "requires" : { + "perl" : "5.008" + } + }, + "test" : { + "requires" : { + "IO::Socket::IP" : "0", + "Net::EmptyPort" : "0", + "Test::Requires" : "0", + "Test::SharedFork" : "0", + "Test::TCP" : "2.08" + } + } + }, + "release_status" : "unstable", + "resources" : { + "bugtracker" : { + "web" : "https://github.com/kazuho/p5-Server-Starter/issues" + }, + "homepage" : "https://github.com/kazuho/p5-Server-Starter", + "repository" : { + "type" : "git", + "url" : "git://github.com/kazuho/p5-Server-Starter.git", + "web" : "https://github.com/kazuho/p5-Server-Starter" + } + }, + "version" : "0.31", + "x_contributors" : [ + "kazuho ", + "yappo ", + "Robert Buels ", + "chromatic ", + "Chia-liang Kao ", + "Masahiro Nagano ", + "Tomoya Kabe ", + "Haruka Iwao ", + "Kazuho Oku ", + "Karen Etheridge ", + "Bugdebugger ", + "Tatsuhiko Miyagawa ", + "Alexandr Ciornii ", + "Tokuhiro Matsuno ", + "Andy Jones ", + "Kazuho Oku " + ] +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/README.md b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/README.md new file mode 100644 index 000000000..c4edcc0c3 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/README.md @@ -0,0 +1,58 @@ +# NAME + +Server::Starter - a superdaemon for hot-deploying server programs + +# SYNOPSIS + + # from command line + % start_server --port=80 my_httpd + + # in my_httpd + use Server::Starter qw(server_ports); + + my $listen_sock = IO::Socket::INET->new( + Proto => 'tcp', + ); + $listen_sock->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind to listening socket:$!"; + + while (1) { + if (my $conn = $listen_sock->accept) { + .... + } + } + +# DESCRIPTION + +It is often a pain to write a server program that supports graceful restarts, with no resource leaks. [Server::Starter](https://metacpan.org/pod/Server::Starter) solves the problem by splitting the task into two. One is [start\_server](https://metacpan.org/pod/start_server), a script provided as a part of the module, which works as a superdaemon that binds to zero or more TCP ports or unix sockets, and repeatedly spawns the server program that actually handles the necessary tasks (for example, responding to incoming commenctions). The spawned server programs under [Server::Starter](https://metacpan.org/pod/Server::Starter) call accept(2) and handle the requests. + +To gracefully restart the server program, send SIGHUP to the superdaemon. The superdaemon spawns a new server program, and if (and only if) it starts up successfully, sends SIGTERM to the old server program. + +By using [Server::Starter](https://metacpan.org/pod/Server::Starter) it is much easier to write a hot-deployable server. Following are the only requirements a server program to be run under [Server::Starter](https://metacpan.org/pod/Server::Starter) should conform to: + +\- receive file descriptors to listen to through an environment variable +\- perform a graceful shutdown when receiving SIGTERM + +A Net::Server personality that can be run under [Server::Starter](https://metacpan.org/pod/Server::Starter) exists under the name [Net::Server::SS::PreFork](https://metacpan.org/pod/Net::Server::SS::PreFork). + +# METHODS + +- server\_ports + + Returns zero or more file descriptors on which the server program should call accept(2) in a hashref. Each element of the hashref is: (host:port|port|path\_of\_unix\_socket) => file\_descriptor. + +- start\_server + + Starts the superdaemon. Used by the `start_server` script. + +# AUTHOR + +Kazuho Oku + +# SEE ALSO + +[Net::Server::SS::PreFork](https://metacpan.org/pod/Net::Server::SS::PreFork) + +# LICENSE + +This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/builder/MyBuilder.pm b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/builder/MyBuilder.pm new file mode 100644 index 000000000..8c8982d45 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/builder/MyBuilder.pm @@ -0,0 +1,13 @@ +package builder::MyBuilder; +use strict; +use warnings; +use base qw(Module::Build); + + +# https://github.com/kazuho/p5-Server-Starter/issues/25 +if (lc $^O eq "mswin32") { + die "OS unsupported\n"; #will result in NA by cpantesters +} + +1; + diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/cpanfile b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/cpanfile new file mode 100644 index 000000000..de18fb959 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/cpanfile @@ -0,0 +1,9 @@ +requires 'perl', '5.008'; + +on test => sub { + requires 'IO::Socket::IP'; + requires 'Net::EmptyPort'; + requires 'Test::Requires'; + requires 'Test::SharedFork'; + requires 'Test::TCP', '2.08'; +}; diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter.pm b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter.pm new file mode 100644 index 000000000..f33d232d9 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter.pm @@ -0,0 +1,603 @@ +package Server::Starter; + +use 5.008; +use strict; +use warnings; +use Carp; +use Fcntl; +use IO::Handle; +use IO::Socket::UNIX; +use POSIX qw(:sys_wait_h); +use Socket (); +use Server::Starter::Guard; +use Fcntl qw(:flock); + +use Exporter qw(import); + +our $VERSION = '0.31'; +our @EXPORT_OK = qw(start_server restart_server stop_server server_ports); + +my @signals_received; + +sub start_server { + my $opts = { + (@_ == 1 ? @$_[0] : @_), + }; + $opts->{interval} = 1 + if not defined $opts->{interval}; + $opts->{signal_on_hup} ||= 'TERM'; + $opts->{signal_on_term} ||= 'TERM'; + $opts->{backlog} ||= Socket::SOMAXCONN(); + for ($opts->{signal_on_hup}, $opts->{signal_on_term}) { + # normalize to the one that can be passed to kill + tr/a-z/A-Z/; + s/^SIG//i; + } + + # prepare args + my $ports = $opts->{port}; + my $paths = $opts->{path}; + croak "either of ``port'' or ``path'' option is mandatory\n" + unless $ports || $paths; + $ports = [ $ports ] + if ! ref $ports && defined $ports; + $paths = [ $paths ] + if ! ref $paths && defined $paths; + croak "mandatory option ``exec'' is missing or is not an arrayref\n" + unless $opts->{exec} && ref $opts->{exec} eq 'ARRAY'; + + # set envs + $ENV{ENVDIR} = $opts->{envdir} + if defined $opts->{envdir}; + $ENV{ENABLE_AUTO_RESTART} = $opts->{enable_auto_restart} + if defined $opts->{enable_auto_restart}; + $ENV{KILL_OLD_DELAY} = $opts->{kill_old_delay} + if defined $opts->{kill_old_delay}; + $ENV{AUTO_RESTART_INTERVAL} = $opts->{auto_restart_interval} + if defined $opts->{auto_restart_interval}; + + # open log file + my $logfh; + if ($opts->{log_file}) { + if ($opts->{log_file} =~ /^\s*\|\s*/s) { + my $cmd = $'; + open $logfh, '|-', $cmd + or die "failed to open pipe:$opts->{log_file}: $!"; + } else { + open $logfh, '>>', $opts->{log_file} + or die "failed to open log file:$opts->{log_file}: $!"; + } + $logfh->autoflush(1); + } + + # create guard that removes the status file + my $status_file_created; + my $status_file_guard = $opts->{status_file} && Server::Starter::Guard->new( + sub { + if ($status_file_created) { + unlink $opts->{status_file}; + } + }, + ); + + print STDERR "start_server (pid:$$) starting now...\n"; + + # start listening, setup envvar + my @sock; + my @sockenv; + for my $hostport (@$ports) { + my ($domain, $sa); + my $fd; + my $sockopts = sub {}; + if ($hostport =~ /^\s*(\d+)(?:\s*=(\d+))?\s*$/) { + # by default, only bind to IPv4 (for compatibility) + $hostport = $1; + $fd = $2; + $domain = Socket::PF_INET; + $sa = pack_sockaddr_in $1, Socket::inet_aton("0.0.0.0"); + } elsif ($hostport =~ /^\s*(?:\[\s*|)([^\]]*)\s*(?:\]\s*|):\s*(\d+)(?:\s*=(\d+))?\s*$/) { + my ($host, $port) = ($1, $2); + $fd = $3; + if ($host =~ /:/) { + # IPv6 + local $@; + eval { + $hostport = "[$host]:$port"; + my $addr = Socket::inet_pton(Socket::AF_INET6(), $host) + or die "failed to resolve host:$host:$!"; + $sa = Socket::pack_sockaddr_in6($port, $addr); + $domain = Socket::PF_INET6(); + }; + if ($@) { + die "No support for IPv6. Please update Perl (or Perl modules)"; + } + $sockopts = sub { + my $sock = shift; + local $@; + eval { + setsockopt $sock, Socket::IPPROTO_IPV6(), Socket::IPV6_V6ONLY(), 1; + }; + }; + } else { + # IPv4 + $domain = Socket::PF_INET; + $hostport = "$host:$port"; + my $addr = gethostbyname $host + or die "failed to resolve host:$host:$!"; + $sa = Socket::pack_sockaddr_in($port, $addr); + } + } else { + croak "invalid ``port'' value:$hostport\n" + } + socket my $sock, $domain, Socket::SOCK_STREAM(), 0 + or die "failed to create socket:$!"; + setsockopt $sock, Socket::SOL_SOCKET, Socket::SO_REUSEADDR(), pack("l", 1); + $sockopts->($sock); + bind $sock, $sa + or die "failed to bind to $hostport:$!"; + listen $sock, $opts->{backlog} + or die "listen(2) failed:$!"; + fcntl($sock, F_SETFD, my $flags = '') + or die "fcntl(F_SETFD, 0) failed:$!"; + if (defined $fd) { + POSIX::dup2($sock->fileno, $fd) + or die "dup2(2) failed(${fd}): $!"; + print STDERR "socket is duplicated to file descriptor ${fd}\n"; + close $sock; + push @sockenv, "$hostport=$fd"; + } else { + push @sockenv, "$hostport=" . $sock->fileno; + } + push @sock, $sock; + } + my $path_remove_guard = Server::Starter::Guard->new( + sub { + -S $_ and unlink $_ + for @$paths; + }, + ); + for my $path (@$paths) { + if (-S $path) { + warn "removing existing socket file:$path"; + unlink $path + or die "failed to remove existing socket file:$path:$!"; + } + unlink $path; + my $saved_umask = umask(0); + my $sock = IO::Socket::UNIX->new( + Listen => $opts->{backlog}, + Local => $path, + ) or die "failed to listen to file $path:$!"; + umask($saved_umask); + fcntl($sock, F_SETFD, my $flags = '') + or die "fcntl(F_SETFD, 0) failed:$!"; + push @sockenv, "$path=" . $sock->fileno; + push @sock, $sock; + } + $ENV{SERVER_STARTER_PORT} = join ";", @sockenv; + $ENV{SERVER_STARTER_GENERATION} = 0; + + # setup signal handlers + _set_sighandler($_, sub { + push @signals_received, $_[0]; + }) for (qw/INT TERM HUP ALRM/); + $SIG{PIPE} = 'IGNORE'; + + # setup status monitor + my ($current_worker, %old_workers, $last_restart_time); + my $update_status = $opts->{status_file} + ? sub { + my $tmpfn = "$opts->{status_file}.$$"; + open my $tmpfh, '>', $tmpfn + or die "failed to create temporary file:$tmpfn:$!"; + $status_file_created = 1; + my %gen_pid = ( + ($current_worker + ? ($ENV{SERVER_STARTER_GENERATION} => $current_worker) + : ()), + map { $old_workers{$_} => $_ } keys %old_workers, + ); + print $tmpfh "$_:$gen_pid{$_}\n" + for sort keys %gen_pid; + close $tmpfh; + rename $tmpfn, $opts->{status_file} + or die "failed to rename $tmpfn to $opts->{status_file}:$!"; + } : sub { + }; + + # now that setup is complete, redirect outputs to the log file (if specified) + if ($logfh) { + STDOUT->flush; + STDERR->flush; + open STDOUT, '>&=', $logfh + or die "failed to dup STDOUT to file: $!"; + open STDERR, '>&=', $logfh + or die "failed to dup STDERR to file: $!"; + close $logfh; + undef $logfh; + } + + # daemonize + if ($opts->{daemonize}) { + my $pid = fork; + die "fork failed:$!" + unless defined $pid; + if ($pid != 0) { + $path_remove_guard->dismiss; + exit 0; + } + # in child process + POSIX::setsid(); + $pid = fork; + die "fork failed:$!" + unless defined $pid; + if ($pid != 0) { + $path_remove_guard->dismiss; + exit 0; + } + # do not close STDIN if `--port=n=0`. + unless (grep /=0$/, @sockenv) { + close STDIN; + open STDIN, '<', '/dev/null' + or die "reopen failed: $!"; + } + } + + # open pid file + my $pid_file_guard = sub { + return unless $opts->{pid_file}; + open my $fh, '>', $opts->{pid_file} + or die "failed to open file:$opts->{pid_file}: $!"; + flock($fh, LOCK_EX) + or die "flock failed($opts->{pid_file}): $!"; + print $fh "$$\n"; + $fh->flush(); + return Server::Starter::Guard->new( + sub { + unlink $opts->{pid_file} + or warn "failed to unlink file:$opts->{pid_file}:$!"; + close $fh; + }, + ); + }->(); + + # setup the start_worker function + my $start_worker = sub { + my $pid; + while (1) { + $ENV{SERVER_STARTER_GENERATION}++; + $pid = fork; + die "fork(2) failed:$!" + unless defined $pid; + if ($pid == 0) { + my @args = @{$opts->{exec}}; + # child process + if (defined $opts->{dir}) { + chdir $opts->{dir} or die "failed to chdir:$opts->{dir}:$!"; + } + { exec { $args[0] } @args }; + print STDERR "failed to exec $args[0]$!"; + exit(255); + } + print STDERR "starting new worker $pid\n"; + sleep $opts->{interval}; + if ((grep { $_ ne 'HUP' } @signals_received) + || waitpid($pid, WNOHANG) <= 0) { + last; + } + print STDERR "new worker $pid seems to have failed to start, exit status:$?\n"; + } + # ready, update the environment + $current_worker = $pid; + $last_restart_time = time; + $update_status->(); + }; + + # setup the wait function + my $wait = sub { + my $block = @signals_received == 0; + my @r; + if ($block && $ENV{ENABLE_AUTO_RESTART}) { + alarm(1); + @r = _wait3($block); + alarm(0); + } else { + @r = _wait3($block); + } + return @r; + }; + + # setup the cleanup function + my $cleanup = sub { + my $sig = shift; + my $term_signal = $sig eq 'TERM' ? $opts->{signal_on_term} : 'TERM'; + $old_workers{$current_worker} = $ENV{SERVER_STARTER_GENERATION}; + undef $current_worker; + print STDERR "received $sig, sending $term_signal to all workers:", + join(',', sort keys %old_workers), "\n"; + kill $term_signal, $_ + for sort keys %old_workers; + while (%old_workers) { + if (my @r = _wait3(1)) { + my ($died_worker, $status) = @r; + print STDERR "worker $died_worker died, status:$status\n"; + delete $old_workers{$died_worker}; + $update_status->(); + } + } + print STDERR "exiting\n"; + }; + + # the main loop + $start_worker->(); + while (1) { + # wait for next signal (or when auto-restart becomes necessary) + my @r = $wait->(); + # reload env if necessary + my %loaded_env = _reload_env(); + my @loaded_env_keys = keys %loaded_env; + local @ENV{@loaded_env_keys} = map { $loaded_env{$_} } (@loaded_env_keys); + $ENV{AUTO_RESTART_INTERVAL} ||= 360 + if $ENV{ENABLE_AUTO_RESTART}; + # restart if worker died + if (@r) { + my ($died_worker, $status) = @r; + if ($died_worker == $current_worker) { + print STDERR "worker $died_worker died unexpectedly with status:$status, restarting\n"; + $start_worker->(); + } else { + print STDERR "old worker $died_worker died, status:$status\n"; + delete $old_workers{$died_worker}; + $update_status->(); + } + } + # handle signals + my $restart; + while (@signals_received) { + my $sig = shift @signals_received; + if ($sig eq 'HUP') { + print STDERR "received HUP, spawning a new worker\n"; + $restart = 1; + last; + } elsif ($sig eq 'ALRM') { + # skip + } else { + return $cleanup->($sig); + } + } + if (! $restart && $ENV{ENABLE_AUTO_RESTART}) { + my $auto_restart_interval = $ENV{AUTO_RESTART_INTERVAL}; + my $elapsed_since_restart = time - $last_restart_time; + if ($elapsed_since_restart >= $auto_restart_interval && ! %old_workers) { + print STDERR "autorestart triggered (interval=$auto_restart_interval)\n"; + $restart = 1; + } elsif ($elapsed_since_restart >= $auto_restart_interval * 2) { + print STDERR "autorestart triggered (forced, interval=$auto_restart_interval)\n"; + $restart = 1; + } + } + # restart if requested + if ($restart) { + $old_workers{$current_worker} = $ENV{SERVER_STARTER_GENERATION}; + $start_worker->(); + print STDERR "new worker is now running, sending $opts->{signal_on_hup} to old workers:"; + if (%old_workers) { + print STDERR join(',', sort keys %old_workers), "\n"; + } else { + print STDERR "none\n"; + } + my $kill_old_delay = defined $ENV{KILL_OLD_DELAY} ? $ENV{KILL_OLD_DELAY} : $ENV{ENABLE_AUTO_RESTART} ? 5 : 0; + if ($kill_old_delay != 0) { + print STDERR "sleeping $kill_old_delay secs before killing old workers\n"; + sleep $kill_old_delay; + } + print STDERR "killing old workers\n"; + kill $opts->{signal_on_hup}, $_ + for sort keys %old_workers; + } + } + + die "unreachable"; +} + +sub restart_server { + my $opts = { + (@_ == 1 ? @$_[0] : @_), + }; + die "--restart option requires --pid-file and --status-file to be set as well\n" + unless $opts->{pid_file} && $opts->{status_file}; + + # get pid + my $pid = do { + open my $fh, '<', $opts->{pid_file} + or die "failed to open file:$opts->{pid_file}:$!"; + my $line = <$fh>; + chomp $line; + $line; + }; + + # function that returns a list of active generations in sorted order + my $get_generations = sub { + open my $fh, '<', $opts->{status_file} + or die "failed to open file:$opts->{status_file}:$!"; + my %gen; + while (my $line = <$fh>) { + if ($line =~ /^(\d+):/) { + $gen{$1} = 1; + } + } + sort { $a <=> $b } keys %gen; + }; + + # wait for this generation + my $wait_for = do { + my @gens = $get_generations->() + or die "no active process found in the status file"; + pop(@gens) + 1; + }; + + # send HUP + kill 'HUP', $pid + or die "failed to send SIGHUP to the server process:$!"; + + # wait for the generation + while (1) { + my @gens = $get_generations->(); + last if scalar(@gens) == 1 && $gens[0] == $wait_for; + sleep 1; + } +} + +sub stop_server { + my $opts = { + (@_ == 1 ? @$_[0] : @_), + }; + die "--stop option requires --pid-file to be set as well\n" + unless $opts->{pid_file}; + + # get pid + open my $fh, '<', $opts->{pid_file} + or die "failed to open file:$opts->{pid_file}:$!"; + my $pid = do { + my $line = <$fh>; + chomp $line; + $line; + }; + + print STDERR "stop_server (pid:$$) stopping now (pid:$pid)...\n"; + + # send TERM + kill 'TERM', $pid + or die "failed to send SIGTERM to the server process:$!"; + + # wait process + flock($fh, LOCK_EX) + or die "flock failed($opts->{pid_file}): $!"; + close $fh; +} + +sub server_ports { + die "no environment variable SERVER_STARTER_PORT. Did you start the process using server_starter?", + unless $ENV{SERVER_STARTER_PORT}; + my %ports = map { + +(split /=/, $_, 2) + } split /;/, $ENV{SERVER_STARTER_PORT}; + \%ports; +} + +sub _reload_env { + my $dn = $ENV{ENVDIR}; + return if !defined $dn or !-d $dn; + my $d; + opendir($d, $dn) or return; + my %env; + while (my $n = readdir($d)) { + next if $n =~ /^\./; + open my $fh, '<', "$dn/$n" or next; + chomp(my $v = <$fh>); + $env{$n} = $v if defined $v; + } + return %env; +} + +our $sighandler_should_die; +my $sighandler_got_sig; + +sub _set_sighandler { + my ($sig, $proc) = @_; + $SIG{$sig} = sub { + $proc->(@_); + $sighandler_got_sig = 1; + die "got signal" + if $sighandler_should_die; + }; +} + +sub _wait3 { + my $block = shift; + my $pid = -1; + if ($block) { + local $@; + eval { + $sighandler_got_sig = 0; + local $sighandler_should_die = 1; + die "exit from eval" + if $sighandler_got_sig; + $pid = wait(); + }; + if ($pid == -1 && $@) { + $! = Errno::EINTR; + } + } else { + $pid = waitpid(-1, WNOHANG); + } + return $pid > 0 ? ($pid, $?) : (); +} + +1; +__END__ + +=head1 NAME + +Server::Starter - a superdaemon for hot-deploying server programs + +=head1 SYNOPSIS + + # from command line + % start_server --port=80 my_httpd + + # in my_httpd + use Server::Starter qw(server_ports); + + my $listen_sock = IO::Socket::INET->new( + Proto => 'tcp', + ); + $listen_sock->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind to listening socket:$!"; + + while (1) { + if (my $conn = $listen_sock->accept) { + .... + } + } + +=head1 DESCRIPTION + +It is often a pain to write a server program that supports graceful restarts, with no resource leaks. L solves the problem by splitting the task into two. One is L, a script provided as a part of the module, which works as a superdaemon that binds to zero or more TCP ports or unix sockets, and repeatedly spawns the server program that actually handles the necessary tasks (for example, responding to incoming commenctions). The spawned server programs under L call accept(2) and handle the requests. + +To gracefully restart the server program, send SIGHUP to the superdaemon. The superdaemon spawns a new server program, and if (and only if) it starts up successfully, sends SIGTERM to the old server program. + +By using L it is much easier to write a hot-deployable server. Following are the only requirements a server program to be run under L should conform to: + +- receive file descriptors to listen to through an environment variable +- perform a graceful shutdown when receiving SIGTERM + +A Net::Server personality that can be run under L exists under the name L. + +=head1 METHODS + +=over 4 + +=item server_ports + +Returns zero or more file descriptors on which the server program should call accept(2) in a hashref. Each element of the hashref is: (host:port|port|path_of_unix_socket) => file_descriptor. + +=item start_server + +Starts the superdaemon. Used by the C script. + +=back + +=head1 AUTHOR + +Kazuho Oku + +=head1 SEE ALSO + +L + +=head1 LICENSE + +This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. + +=cut diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter/Guard.pm b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter/Guard.pm new file mode 100644 index 000000000..f25b49c17 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/lib/Server/Starter/Guard.pm @@ -0,0 +1,21 @@ +package Server::Starter::Guard; + +use strict; +use warnings; + +sub new { + my ($klass, $handler) = @_; + return bless { + handler => $handler, + active => 1, + }, $klass; +} + +sub dismiss { shift->{active} = 0 } + +sub DESTROY { + my $self = shift; + $self->{active} && $self->{handler}->(); +} + +1; diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/minil.toml b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/minil.toml new file mode 100644 index 000000000..4dac44941 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/minil.toml @@ -0,0 +1,2 @@ +[build] +build_class = "builder::MyBuilder" diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/script/start_server b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/script/start_server new file mode 100755 index 000000000..20dd9fab7 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/script/start_server @@ -0,0 +1,179 @@ +#!perl + +use strict; +use warnings; + +use Getopt::Long; +use Pod::Usage; +use Server::Starter qw(start_server restart_server stop_server); + +my %opts = ( + port => [], + path => [], +); + +GetOptions( + map { + $_ => do { + my $name = (split '=', $_, 2)[0]; + $name =~ s/-/_/g; + $opts{$name} ||= undef; + ref($opts{$name}) ? $opts{$name} : \$opts{$name}; + }, + } qw(port=s path=s interval=i log-file=s pid-file=s dir=s signal-on-hup=s signal-on-term=s + backlog=i envdir=s enable-auto-restart=i daemonize auto-restart-interval=i kill-old-delay=i + status-file=s restart stop help version), +) or exit 1; +pod2usage( + -exitval => 0, + -verbose => 1, +) if $opts{help}; +if ($opts{version}) { + print "$Server::Starter::VERSION\n"; + exit 0; +} + +if ($opts{restart}) { + restart_server(%opts); + exit 0; +} + +if ($opts{stop}) { + stop_server(%opts); + exit 0; +} + +if ($opts{daemonize}) { + die "Usage: --log-file option must be specified together with --deamonize\n" + unless defined $opts{log_file}; +} + +# validate options +die "server program not specified\n" + unless @ARGV; + +start_server( + %opts, + exec => \@ARGV, +); + +__END__ + +=head1 NAME + +start_server - a superdaemon for hot-deploying server programs + +=head1 SYNOPSIS + + start_server [options] -- server-prog server-arg1 server-arg2 ... + + # start Plack using Starlet listening at TCP port 8000 + start_server --port=8000 -- plackup -s Starlet --max-workers=100 index.psgi + +=head1 DESCRIPTION + +This script is a frontend of L. For more information please refer to the documentation of the module. + +=head1 OPTIONS + +=head2 --port=(port|host:port|port=fd|host:port=fd) + +TCP port to listen to (if omitted, will not bind to any ports) + +If host is not specified, then the program will bind to the default address of IPv4 ("0.0.0.0"). +Square brackets should be used to specify an IPv6 address (e.g. --port=[::1]:8080) + +If fd is specified, then start_server allocates the socket at the given number. + +=head2 --path=path + +path at where to listen using unix socket (optional) + +=head2 --dir=path + +working directory, start_server do chdir to before exec (optional) + +=head2 --interval=seconds + +minimum interval to respawn the server program (default: 1) + +=head2 --signal-on-hup=SIGNAL + +name of the signal to be sent to the server process when start_server receives a SIGHUP (default: SIGTERM). If you use this option, be sure to also use C<--signal-on-term> below. + +=head2 --signal-on-term=SIGNAL + +name of the signal to be sent to the server process when start_server receives a SIGTERM (default: SIGTERM) + +=head2 --pid-file=filename + +if set, writes the process id of the start_server process to the file + +=head2 --status-file=filename + +if set, writes the status of the server process(es) to the file + +=head2 --envdir=ENVDIR + +directory that contains environment variables to the server processes. +It is intended for use with C in C. +This can be overwritten by environment variable C. + +=head2 --log-file=file + +=head2 --log-file="| cmd args..." + +if set, redirects STDOUT and STDERR to given file or command + +=head2 --daemonize + +deamonizes the server (by doing fork,setsid,fork). Must be used together with C<--log-file>. + +=head2 --enable-auto-restart + +enables automatic restart by time. +This can be overwritten by environment variable C. + +=head2 --auto-restart-interval=seconds + +automatic restart interval (default 360). It is used with C<--enable-auto-restart> option. +This can be overwritten by environment variable C. + +=head2 --kill-old-delay=seconds + +time to suspend to send a signal to the old worker. The default value is 5 when C<--enable-auto-restart> is set, 0 otherwise. +This can be overwritten by environment variable C. + +=head2 --backlog=size + +specifies a listen backlog parameter, whose default is SOMAXCONN (usually 128 on Linux). While SOMAXCONN is enough for most loads, large backlog is required for heavy loads. + +=head2 --restart + +this is a wrapper command that reads the pid of the start_server process from --pid-file, sends SIGHUP to the process and waits until the server(s) of the older generation(s) die by monitoring the contents of the --status-file + +=head2 --stop + +this is a wrapper command that reads the pid of the start_server process from --pid-file, sends SIGTERM to the process. + +=head2 --help + +prints this help + +=head2 --version + +prints the version number + +=head1 AUTHOR + +Kazuho Oku + +=head1 SEE ALSO + +L + +=head1 LICENSE + +This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. + +=cut diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/00-base.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/00-base.t new file mode 100644 index 000000000..4e4dbb0a9 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/00-base.t @@ -0,0 +1,8 @@ +use strict; +use warnings; + +use Test::More tests => 1; + +BEGIN { + use_ok('Server::Starter'); +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter-echod.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter-echod.pl new file mode 100755 index 000000000..f35d200d9 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter-echod.pl @@ -0,0 +1,35 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +my $sigfn = shift @ARGV; +open my $sigfh, '>', $sigfn + or die "could not open file:$sigfn:$!"; + +$SIG{TERM} = $SIG{USR1} = sub { + my $signame = shift; + print $sigfh $signame; + sleep 2; + exit 0; +}; + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + my $buf; + while ($conn->sysread($buf, 1048576) > 0) { + $conn->syswrite("$$:$buf"); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter.t new file mode 100644 index 000000000..f4b0d4162 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/01-starter.t @@ -0,0 +1,82 @@ +use strict; +use warnings; + +use File::Temp (); +use Test::TCP; +use Test::More tests => 28; + +use Server::Starter qw(start_server); + +$SIG{PIPE} = sub {}; + +my $tempdir = File::Temp::tempdir(CLEANUP => 1); + +for my $signal_on_hup ('TERM', 'USR1') { + + test_tcp( + server => sub { + my $port = shift; + start_server( + port => $port, + exec => [ + $^X, qw(t/01-starter-echod.pl), "$tempdir/signame", + ], + status_file => "$tempdir/status", + ($signal_on_hup ne 'TERM' + ? (signal_on_hup => $signal_on_hup) : ()), + ); + }, + client => sub { + my ($port, $server_pid) = @_; + my $buf; + #sleep 1; + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'connect'); + # check response and get pid + is($sock->syswrite("hello"), 5, 'write'); + ok($sock->sysread($buf, 1048576), 'read'); + undef $sock; + like($buf, qr/^\d+:hello$/, 'read'); + $buf =~ /^(\d+):/; + my $worker_pid = $1; + # switch to next gen + sleep 3; + my $status = get_status(); + like(get_status(), qr/^1:\d+\n$/s, 'status before restart'); + kill 'HUP', $server_pid; + sleep 2; + like(get_status(), qr/^1:\d+\n2:\d+$/s, 'status during restart'); + sleep 2; + like(get_status(), qr/^2:\d+\n$/s, 'status after restart'); + is( + do { + open my $fh, '<', "$tempdir/signame" + or die $!; + <$fh>; + }, + $signal_on_hup, + 'signal sent on hup', + ); + $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'reconnect'); + is($sock->syswrite("hello"), 5, 'write after switching'); + ok($sock->sysread($buf, 1048576), 'read after switching'); + like($buf, qr/^\d+:hello$/, 'read after swiching (format)'); + isnt($buf, "$worker_pid:hello", 'pid should have changed'); + }, + ); + + ok ! -e "$tempdir/status", 'no more status file'; +} + +sub get_status { + open my $fh, '<', "$tempdir/status" + or die "failed to open file:$tempdir/status:$!"; + do { undef $/; <$fh> }; +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail-server.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail-server.pl new file mode 100755 index 000000000..6913bc8eb --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail-server.pl @@ -0,0 +1,32 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +$SIG{TERM} = sub { + exit 0; +}; + +my $gen = $ENV{SERVER_STARTER_GENERATION}; + +if ($gen == 1 || 3 <= $gen && $gen < 5) { + # emulate startup failure + exit 1; +} + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + $conn->syswrite($gen); + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail.t new file mode 100644 index 000000000..0985534b2 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/02-startfail.t @@ -0,0 +1,61 @@ +use strict; +use warnings; + +use Test::TCP; +use Test::More tests => 9; + +use Server::Starter qw(start_server); + +test_tcp( + server => sub { + my $port = shift; + start_server( + port => $port, + exec => [ $^X, qw(t/02-startfail-server.pl) ], + ); + }, + client => sub { + my ($port, $server_pid) = @_; + my $buf; + sleep 3; + { + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'connect'); + # check generation + ok($sock->sysread($buf, 1048576), 'read'); + is($buf, 2, 'check generation'); + } + # request restart, that will fail + kill 'HUP', $server_pid; + sleep 1; + { + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'connect'); + ok( + $sock->sysread($buf, 1048576), + 'read while worker is failing to reboot', + ); + is($buf, 2, 'check generation'); + } + # wait until server succeds in reboot + sleep 5; + { + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'connect'); + ok( + $sock->sysread($buf, 1048576), + 'read after worker succeeds to reboot', + ); + is($buf, 5, 'check generation'); + } + }, +); diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix-echod.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix-echod.pl new file mode 100644 index 000000000..a54677475 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix-echod.pl @@ -0,0 +1,22 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::UNIX; +use Server::Starter qw(server_ports); + +my $listener = IO::Socket::UNIX->new() + or die "failed to create unix socket:$!"; +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failde to bind to listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + while ($conn->sysread(my $buf, 1048576) > 0) { + $conn->syswrite($buf); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix.t new file mode 100644 index 000000000..965de6b68 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/03-starter-unix.t @@ -0,0 +1,40 @@ +use strict; +use warnings; + +use File::Temp (); +use IO::Socket::UNIX; +use Test::More tests => 4; +use Test::SharedFork; + +use Server::Starter qw(start_server); + +$SIG{PIPE} = sub {}; + +my $sockfile = File::Temp::tmpnam(); + +my $pid = fork; +die "fork failed: $!" + unless defined $pid; +if ($pid == 0) { + # child + start_server( + path => $sockfile, + exec => [ $^X, qw(t/03-starter-unix-echod.pl) ], + ); + exit 0; +} else { + # parent + sleep 1 + until -e $sockfile; + my $sock = IO::Socket::UNIX->new( + Peer => $sockfile, + ) or die "failed to connect to unix socket:$!"; + is $sock->syswrite('hello', 5), 5, 'write'; + is $sock->sysread(my $buf, 5), 5, 'read length'; + is $buf, 'hello', 'read data'; + kill 'TERM', $pid; + while (wait != $pid) {} + ok ! -e $sockfile, 'socket file removed after shutdown'; +} + +unlink $sockfile; diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/04-starter-dir.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/04-starter-dir.t new file mode 100644 index 000000000..5f1b3968a --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/04-starter-dir.t @@ -0,0 +1,54 @@ +use strict; +use warnings; + +use File::Temp (); +use Test::More tests => 1; +use Net::EmptyPort qw/empty_port/; +use IO::Select; +use Server::Starter qw(start_server); + +$SIG{PIPE} = sub {}; + +pipe my $logrh, my $logwh + or die "Died: failed to create pipe:$!"; +my $port = empty_port + or die "could not get any port"; +my $tempdir = File::Temp::tempdir(CLEANUP => 0); +open(my $fh, '>', "$tempdir/dir_status") or die "$!"; +close($fh); + +my $pid = fork; + +if ( ! defined $pid ) { + die "Died: fork failed: $!"; +} +elsif ( $pid == 0 ) { + close $logrh; + open STDOUT, '>&', $logwh + or die "Died: failed to redirect STDOUT"; + close $logwh; + start_server( + port => $port, #not use + exec => [ + $^X, '-e', 'printf "%s\n", -f "dir_status" ? "OK" : "NG"; sleep(1)' + ], + dir => $tempdir + ); + exit(255); +} + +close $logwh; +my $result; +my $s = IO::Select->new($logrh); +my @ready = $s->can_read(10); +die "could not read logs from pipe" unless @ready; +sysread($logrh, my $buf, 65536); +like($buf, qr/OK\W/); + +kill 'TERM', $pid; +while (wait != $pid) {} + +unlink "$tempdir/status"; +rmdir $tempdir; + + diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay-echod.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay-echod.pl new file mode 100755 index 000000000..5b48ca559 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay-echod.pl @@ -0,0 +1,35 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +my $sigfn = shift @ARGV; +open my $sigfh, '>', $sigfn + or die "could not open file:$sigfn:$!"; + +$SIG{TERM} = $SIG{USR1} = sub { + my $signame = shift; + print $sigfh $signame; + sleep 1; + exit 0; +}; + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + my $buf; + while ($conn->sysread($buf, 1048576) > 0) { + $conn->syswrite("$$:$buf"); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay.t new file mode 100644 index 000000000..19856af52 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/05-killolddelay.t @@ -0,0 +1,87 @@ +use strict; +use warnings; + +use File::Temp (); +use Test::TCP; +use Test::More tests => 28; + +use Server::Starter qw(start_server); + +$SIG{PIPE} = sub {}; + +my $tempdir = File::Temp::tempdir(CLEANUP => 1); + +for my $signal_on_hup ('TERM', 'USR1') { + + test_tcp( + server => sub { + my $port = shift; + start_server( + port => $port, + exec => [ + $^X, qw(t/05-killolddelay-echod.pl), "$tempdir/signame", + ], + status_file => "$tempdir/status", + enable_auto_restart => 0, + kill_old_delay => 3, + ($signal_on_hup ne 'TERM' + ? (signal_on_hup => $signal_on_hup) : ()), + ); + }, + client => sub { + my ($port, $server_pid) = @_; + my $buf; + #sleep 1; + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'connect'); + # check response and get pid + is($sock->syswrite("hello"), 5, 'write'); + ok($sock->sysread($buf, 1048576), 'read'); + undef $sock; + like($buf, qr/^\d+:hello$/, 'read'); + $buf =~ /^(\d+):/; + my $worker_pid = $1; + # switch to next gen + sleep 2; + my $status = get_status(); + like(get_status(), qr/^1:\d+\n$/s, 'status before restart'); + kill 'HUP', $server_pid; + sleep 4; + like(get_status(), qr/^1:\d+\n2:\d+$/s, 'status during restart'); + # Child process has finished in 2 seconds but the parent + # checks and calls waitpid every second, so wait for an + # additional 1 second. + sleep 3; + like(get_status(), qr/^2:\d+\n$/s, 'status after restart'); + is( + do { + open my $fh, '<', "$tempdir/signame" + or die $!; + <$fh>; + }, + $signal_on_hup, + 'signal sent on hup', + ); + $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'reconnect'); + is($sock->syswrite("hello"), 5, 'write after switching'); + ok($sock->sysread($buf, 1048576), 'read after switching'); + like($buf, qr/^\d+:hello$/, 'read after swiching (format)'); + isnt($buf, "$worker_pid:hello", 'pid should have changed'); + }, + ); + + ok ! -e "$tempdir/status", 'no more status file'; +} + +sub get_status { + open my $fh, '<', "$tempdir/status" + or die "failed to open file:$tempdir/status:$!"; + do { undef $/; <$fh> }; +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart-echod.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart-echod.pl new file mode 100755 index 000000000..2d71a654f --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart-echod.pl @@ -0,0 +1,34 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +my $sigfn = shift @ARGV; +open my $sigfh, '>', $sigfn + or die "could not open file:$sigfn:$!"; + +$SIG{TERM} = $SIG{USR1} = sub { + my $signame = shift; + print $sigfh $signame; + exit 0; +}; + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + my $buf; + while ($conn->sysread($buf, 1048576) > 0) { + $conn->syswrite("$$:$buf"); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart.t new file mode 100644 index 000000000..1e1f86582 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/06-autorestart.t @@ -0,0 +1,84 @@ +use strict; +use warnings; + +use File::Temp (); +use Test::TCP; +use Test::More tests => 28; + +use Server::Starter qw(start_server); + +$SIG{PIPE} = sub {}; + +my $tempdir = File::Temp::tempdir(CLEANUP => 1); + +for my $signal_on_hup ('TERM', 'USR1') { + + test_tcp( + server => sub { + my $port = shift; + start_server( + port => $port, + exec => [ + $^X, qw(t/05-killolddelay-echod.pl), "$tempdir/signame", + ], + status_file => "$tempdir/status", + enable_auto_restart => 1, + auto_restart_interval => 6, + kill_old_delay => 2, + ($signal_on_hup ne 'TERM' + ? (signal_on_hup => $signal_on_hup) : ()), + ); + }, + client => sub { + my ($port, $server_pid) = @_; + my $buf; + #sleep 1; + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'connect'); + # check response and get pid + is($sock->syswrite("hello"), 5, 'write'); + ok($sock->sysread($buf, 1048576), 'read'); + undef $sock; + like($buf, qr/^\d+:hello$/, 'read'); + $buf =~ /^(\d+):/; + my $worker_pid = $1; + # switch to next gen + sleep 2; + my $status = get_status(); + like(get_status(), qr/^1:\d+\n$/s, 'status before auto-restart'); + sleep 7; # new worker spawn at 7 (since start, interval(1) + auto_restart_interval(6)), status updated at 8 (7 + interval(1)), old dies at 11 (8 + kill_old_delay(2) + sleep(1) in the child source code) + like(get_status(), qr/^1:\d+\n2:\d+$/s, 'status during transient state'); + sleep 3; + like(get_status(), qr/^2:\d+\n$/s, 'status after auto-restart'); + is( + do { + open my $fh, '<', "$tempdir/signame" + or die $!; + <$fh>; + }, + $signal_on_hup, + 'signal sent on hup', + ); + $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'reconnect'); + is($sock->syswrite("hello"), 5, 'write after switching'); + ok($sock->sysread($buf, 1048576), 'read after switching'); + like($buf, qr/^\d+:hello$/, 'read after swiching (format)'); + isnt($buf, "$worker_pid:hello", 'pid should have changed'); + }, + ); + + ok ! -e "$tempdir/status", 'no more status file'; +} + +sub get_status { + open my $fh, '<', "$tempdir/status" + or die "failed to open file:$tempdir/status:$!"; + do { undef $/; <$fh> }; +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir-print.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir-print.pl new file mode 100755 index 000000000..22cb01af0 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir-print.pl @@ -0,0 +1,29 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +$SIG{TERM} = $SIG{USR1} = sub { + exit 0; +}; + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + my $s = ""; + for my $envkey (keys %ENV) { + $s .= $envkey . "=" . $ENV{$envkey} . "\n"; + } + $conn->syswrite($s); + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir.t new file mode 100644 index 000000000..f10fbade4 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/07-envdir.t @@ -0,0 +1,77 @@ +use strict; +use warnings; + +use File::Temp (); +use Test::TCP; +use Test::More tests => 4; + +use Server::Starter qw(start_server); + +$SIG{PIPE} = sub {}; + +my $tempdir = File::Temp::tempdir(CLEANUP => 1); + +# Prepare envdir +mkdir "$tempdir/env" or die $!; +open my $envfh, ">", "$tempdir/env/FOO" or die $!; +print $envfh "foo-value1"; +close $envfh; +undef $envfh; +sleep 1; + +test_tcp( + server => sub { + my $port = shift; + start_server( + port => $port, + exec => [ + $^X, qw(t/07-envdir-print.pl), + ], + status_file => "$tempdir/status", + envdir => "$tempdir/env", + ); + }, + client => sub { + my ($port, $server_pid) = @_; + #sleep 1; + my $fetch_env = sub { + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ) or die $!; + my $buf; + $sock->sysread($buf, 1048576) + or die $!; + undef $sock; + $buf; + }; + my $restart = sub { + sleep 1; + kill "HUP", $server_pid; + sleep 2; + }; + # Initial worker does not read envdir + my $buf = $fetch_env->(); + ok($buf !~ qr/^FOO=foo-value1$/m, 'changed env'); + # rewrite envdir + open my $envfh, ">", "$tempdir/env/FOO" or die $!; + print $envfh "foo-value2"; + close $envfh; + undef $envfh; + # switch to next gen + $restart->(); + # new worker reads the rewritten envdir + $buf = $fetch_env->(); + ok($buf =~ /^FOO=foo-value2$/m, 'changed env'); + # remove the env file and check that the removal gets reflected + unlink "$tempdir/env/FOO" + or die $!; + # switch to next gen + $restart->(); + # new worker reads the rewritten envdir + $buf = $fetch_env->(); + ok($buf !~ /^FOO=foo-value2$/m, 'removed env'); + }, +); + +ok ! -e "$tempdir/status", 'no more status file'; diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/08-wait3.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/08-wait3.t new file mode 100644 index 000000000..e13b6e8b3 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/08-wait3.t @@ -0,0 +1,39 @@ +use strict; +use warnings; +use Test::More; +use Server::Starter; + +my $gotsig = 0; +Server::Starter::_set_sighandler('USR1', sub { + warn "got SIGUSR1"; + ++$gotsig; +}); + +my $pid = fork; +die "fork failed:$!" + unless defined $pid; +if ($pid == 0) { + # child process, send signal twice + sleep 1; + kill 'USR1', getppid; + sleep 1; + kill 'USR1', getppid; + sleep 10; + die "child process not killed"; +} + +my @r = Server::Starter::_wait3(0); +ok ! @r, "nonblocking wait returns without pid"; + +for (my $i = 1; $i <= 2; ++$i) { + @r = Server::Starter::_wait3(1); + is $gotsig, $i, "woke up after signal (count: $i)"; + ok ! @r, "child is alive"; +} + +kill 'KILL', $pid; +@r = Server::Starter::_wait3(1); +is $gotsig, 2, "did not receive signal"; +is $r[0], $pid, "child died"; + +done_testing; diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/09-guard.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/09-guard.t new file mode 100644 index 000000000..a51903a4e --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/09-guard.t @@ -0,0 +1,31 @@ +use strict; +use warnings; +use Test::More; + +use_ok("Server::Starter::Guard"); + +subtest "guard is called when it goes out of scope" => sub { + my $cnt = 0; + + my $guard = Server::Starter::Guard->new(sub { + $cnt++; + }); + + is $cnt, 0; + undef $guard; + is $cnt, 1; +}; + +subtest "guard can be dismissed" => sub { + my $cnt = 0; + + my $guard = Server::Starter::Guard->new(sub { + $cnt++; + }); + + $guard->dismiss; + undef $guard; + is $cnt, 0; +}; + +done_testing; diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr-server.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr-server.pl new file mode 100755 index 000000000..279c59673 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr-server.pl @@ -0,0 +1,26 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +$SIG{TERM} = $SIG{USR1} = sub { + exit 0; +}; +$SIG{HUP} = sub {}; + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + close $conn; + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr.t new file mode 100644 index 000000000..16a76768c --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/10-bindaddr.t @@ -0,0 +1,55 @@ +use strict; +use warnings; +use Test::More; +use Test::Requires qw(IO::Socket::IP); +use Net::EmptyPort qw(can_bind empty_port); +use Server::Starter qw(start_server); + +plan skip_all => 'IPv6 not available' + unless can_bind('::1'); + +my $port = empty_port; + +sub doit { + my ($bind_addr, $other_addr) = @_; + my $pid = fork; + die "fork failed:$!" + unless defined $pid; + if ($pid == 0) { + # server + start_server( + port => "[$bind_addr]:$port", + exec => [ + $^X, qw(t/10-bindaddr-server.pl), + ], + ); + exit 0; + } + # client + sleep 1; + my $sock = IO::Socket::IP->new( + PeerHost => $bind_addr, + PeerPort => $port, + Proto => 'tcp', + ); + ok($sock, "connected to bindaddr"); + $sock->sysread(my $buf, 1024); # wait for disconnect + undef $sock; + $sock = IO::Socket::IP->new( + PeerHost => $other_addr, + PeerPort => $port, + Proto => 'tcp', + ); + ok ! defined $sock, "cannot connect to other addr"; + kill 'TERM', $pid; + wait(); +} + +subtest "v4" => sub { + doit("127.0.0.1", "::1"); +}; +subtest "v6" => sub { + doit("::1", "127.0.0.1"); +}; + +done_testing; diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd-server.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd-server.pl new file mode 100644 index 000000000..f05282ee1 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd-server.pl @@ -0,0 +1,25 @@ +#! /usr/bin/perl +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +die "fd must be zero" unless ((values %{server_ports()})[0]) eq 0; + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen(0, 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + my $buf; + while ($conn->sysread($buf, 1048576) > 0) { + $conn->syswrite("$$:$buf"); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd.t new file mode 100644 index 000000000..7e3b820be --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/11-specified-fd.t @@ -0,0 +1,40 @@ +use strict; +use warnings; + +use File::Temp (); +use Test::TCP; +use Test::More tests => 4; + +use Server::Starter qw(start_server); + +$SIG{PIPE} = sub {}; + +test_tcp( + server => sub { + my $port = shift; + start_server( + port => "$port=0", + exec => [ + $^X, qw(t/11-specified-fd-server.pl) + ], + ); + exit 0; + }, + client => sub { + my ($port, $server_pid) = @_; + my $buf; + #sleep 1; + my $sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1:$port", + Proto => 'tcp', + ); + ok($sock, 'connect'); + # check response and get pid + is($sock->syswrite("hello"), 5, 'write'); + ok($sock->sysread($buf, 1048576), 'read'); + undef $sock; + like($buf, qr/^\d+:hello$/, 'read'); + kill $server_pid; + }, +); + diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop-server.pl b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop-server.pl new file mode 100755 index 000000000..ace7e5db4 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop-server.pl @@ -0,0 +1,24 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use lib qw(blib/lib lib); + +use IO::Socket::INET; +use Server::Starter qw(server_ports); + +my $listener = IO::Socket::INET->new( + Proto => 'tcp', +); +$listener->fdopen((values %{server_ports()})[0], 'w') + or die "failed to bind listening socket:$!"; + +while (1) { + if (my $conn = $listener->accept) { + my $buf; + while ($conn->sysread($buf, 1048576) > 0) { + $conn->syswrite("$$:$buf"); + } + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop.t new file mode 100644 index 000000000..6d40c25f5 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/12-stop.t @@ -0,0 +1,50 @@ +use strict; +use warnings; +use utf8; +use Test::More; +use Server::Starter qw(start_server restart_server stop_server); +use File::Temp qw(tempdir); +use Test::TCP; + +plan tests => 2; + +my $dir = tempdir( CLEANUP => 1 ); +my $pidfile = "$dir/pid"; + +test_tcp( + server => sub { + my $port = shift; + + start_server( + pid_file => $pidfile, + daemonize => 1, + port => $port, + exec => [ $^X, 't/12-stop-server.pl' ], + ); + exit 0; + }, + client => sub { + my $port = shift; + + while (!-s $pidfile) { + note 'pid file is not available'; + sleep 1; # wait pid file + } + + my $pid = do { + open my $fh, '<', $pidfile + or die "Cannot open $pidfile: $!"; + local $/; + <$fh>; + }; + note "PID=$pid"; + is(kill(0, $pid), 1, 'there is a process'); + + stop_server( + pid_file => $pidfile, + port => $port, + ); + ok((!-e $pidfile), 'pid file was unlinked'); + }, +); + diff --git a/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/13-unix-daemonize.t b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/13-unix-daemonize.t new file mode 100644 index 000000000..ab401dc30 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-Server-Starter/t/13-unix-daemonize.t @@ -0,0 +1,68 @@ +use strict; +use warnings; +use utf8; +use Test::More; +use Server::Starter qw(start_server stop_server); +use Server::Starter::Guard; +use File::Temp qw(tempdir); + +plan tests => 1; + +my $dir = tempdir( CLEANUP => 1 ); +my $pidfile = "$dir/pid"; +my $sockfile = "$dir/server.sock"; + +fork_ok( + child => sub { + start_server( + pid_file => $pidfile, + daemonize => 1, + path => $sockfile, + exec => [ $^X, qw(t/03-starter-unix-echod.pl) ], + ); + }, + + parent => sub { + my $guard = Server::Starter::Guard->new(sub { + stop_server( pid_file => $pidfile ); + }); + + wait_for(sub { -e $pidfile }) + or BAIL_OUT("Pidfile '$pidfile' was not created in a timely fashion"); + + wait_for(sub { -e $sockfile }) + or BAIL_OUT("Socket '$sockfile' was not created in a timely fashion"); + + ok(-e $sockfile, 'there is a socket'); + }, +) or die "fork failed: $!"; + +sub fork_ok { + my (%args) = @_; + + my $pid = fork; + return unless defined $pid; + if ($pid == 0) { + $args{child}->(); + } + else { + $args{parent}->($pid); + } + + return 1; +} + +sub wait_for { + my ($code, %opts) = @_; + + my $times = $opts{times} || 10; + my $every = $opts{every} || 1; + + while ( $times > 0 ) { + return 1 if $code->(); + $times--; + sleep $every; + } + + return 0; +} diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Changes b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Changes new file mode 100644 index 000000000..e8a0c6a05 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Changes @@ -0,0 +1,102 @@ +0.14 2012-03-26 + - No functional changes in this release + - Don't rely on hash keys being ordered in tests, Perl 5.18 introduces a + per process randomization. + +0.13 2011-02-12 + - No functional changes in this release + - Added eg/runfcgi.pl, contributed by Paul Evans (LeoNerd) + - Added eg/server.pl + +0.12 2010-07-14 + - Added Net::FastCGI::IO + +0.11 2010-04-09 + - Documented the goals with this project/distribution + - Net::FastCGI::Protocol + - Fixed dump_record() to properly escape FCGI_NameValuePair header + - Added tests for this + - Added get_record_length() + - Added documentation and tests + - Changed parse_record() to return a list in list context, this makes it + more consistent with parse_header() + - Added documentation and tests this change + - Changed dump_record() to accept a string of octets (old behavior is + still supported but deprecated, please change function call to dump_record_body()) + This change makes it more consistent with parse_record_body(). + - Added documentation and tests this change + +0.10 2010-04-02 + - Minor optimizations to avoid unnecessary copying of '$content' strings + - Fixed dump_record() to properly insert ellipsis when truncating stream content + - added tests for this + - Added more tests for dump_record() + +0.09 2010-03-31 + - Added check_params() and dump_record() + - added documentation + - added test for check_params() and dump_record() (incomplete) + - Minor optimizations to build_stream() and build_record() + +0.08 2010-02-16 + - Documented return value of get_type_name(), get_role_name() + and get_protocol_status_name(). + - Changed test prerequisite from Test::BinaryData to Test::HexString. + - Corrected note about AnyEvent::FCGI, it's capable of multiplexing. + +0.07 2010-02-10 + - Added notes about existing Perl implementations. + - Added references to specifications and white papers. + - Minor internal "cosmetic" changes + - Added more tests for build_begin_request() and build_end_request() + +0.06 2010-02-09 + - NOTE: Changed application_status to app_status, this affects users of + parse_record() or parse_record_body(). Former was unnecessarily verbose. + Latter also matches the component name of FCGI_BeginRequestBody struct. + - Added build_begin_request() and build_end_request() + - added documentation and tests for these + +0.05 2010-02-06 + - Net::FastCGI::Constant + - Improved documentation + - Added @FCGI_TYPE_NAME, @FCGI_ROLE_NAME and @FCGI_PROTOCOL_STATUS_NAME + - Re-factored Net::FastCGI::Protocol to use these. + - Renamed FCGI_MAX_LEN to FCGI_MAX_CONTENT_LEN + - FCGI_MAX_LEN is deprecated and will be removed in a future version. + - Net::FastCGI::Protocol + - Fixed parse_record() and parse_record_body() to properly detect malformed + stream records. + - Added tests for this. + - Increased segment size in build_stream() from 8192 to 32760 to reflect modern + socket buffers. + - Updated tests + - Documented segment size + - Documented scalar return value of parse_header() + - Minor documentation updates + +0.04 2010-01-30 + - Added parse_record() and parse_record_body() + - Added tests for these + - Added docs (incomplete) + - Cleaned up exception messages. Protocol exceptions now have a FastCGI prefix + - Fixed parse_params() to correctly detect incomplete FCGI_NameValuePair's + - added tests for this + - Added tests for build_stream() + - Changed parse_header() to return a hash reference in scalar context + - added tests for this + - Coverage ~90% (stmt:100.0 bran:96.9 cond:92.9) + - More tests (and docs) needed to cover all cases + +0.03 2010-01-23 + - Fixed package loading in Net::FastCGI::Protocol + +0.02 2010-01-23 + - Removed object oriented implementation, it will eventually be released as + a separate distribution with different prerequisites. + - Removed unnecessary functions from Net::FastCGI::Protocol + - Re-factored internals of Net::FastCGI::Protocol to be more performant. + - No major changes planned for existing API in Net::FastCGI::Protocol + +0.01_01 2009-10-17 + - Initial release. diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/MANIFEST.SKIP b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/MANIFEST.SKIP new file mode 100644 index 000000000..b6d0b82f6 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/MANIFEST.SKIP @@ -0,0 +1,25 @@ +^_build +^Build$ +^blib +~$ +\.bak$ +CVS +\.svn +\.DS_Store +cover_db +\..*\.sw.?$ +^Makefile$ +^pm_to_blib$ +^MakeMaker-\d +^blibdirs$ +\.old$ +^#.*#$ +^\.# +^TODO$ +^PLANS$ +^doc/ +^dev/ +^benchmarks +^\._.*$ +\.shipit +\.git.* diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Makefile.PL b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Makefile.PL new file mode 100644 index 000000000..0b1d1f481 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/Makefile.PL @@ -0,0 +1,19 @@ +use strict; +use inc::Module::Install; + +name 'Net-FastCGI'; +perl_version '5.006'; +all_from 'lib/Net/FastCGI.pm'; +repository 'http://github.com/chansen/p5-net-fastcgi'; + +requires 'Carp' => '0'; +requires 'Exporter' => '0'; + +test_requires 'Test::More' => '0.47'; +test_requires 'Test::Exception' => '0'; +test_requires 'Test::HexString' => '0'; + +tests 't/*.t t/*/*.t'; + +WriteAll; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/README b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/README new file mode 100644 index 000000000..4eea9d556 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/README @@ -0,0 +1,113 @@ +NAME + Net::FastCGI - FastCGI Toolkit + +DESCRIPTION + This distribution aims to provide a complete API for working with the + FastCGI protocol. + + The primary goal is to provide a function oriented and object oriented + API which are not tied to a specific I/O model or framework. + + Secondary goal is to provide higher level tools/API which can be used + for debugging and interoperability testing. + +PROGRESS + The function oriented API is considered feature complete. + Net::FastCGI::Protocol provides functions to build and parse all FastCGI + v1.0 messages, also provided is a few convenient higher level functions + such as "build_begin_request()", "build_end_request()", "parse_record()" + and "dump_record()". + + Work has begun on object oriented implementation and a simple blocking + I/O class which is intended for testing and debugging. + +PACKAGES + Net::FastCGI::Constant + FastCGI protocol constants. + + Net::FastCGI::IO + Provides functions to read and write FastCGI messages. + + Net::FastCGI::Protocol + Provides functions to build and parse FastCGI messages. + +ENVIRONMENT + Environment variable "NET_FASTCGI_PP" can be set to a true value before + loading this package to disable usage of XS implementation. + +PREREQUISITES + Run-Time + perl 5.6 or greater. + Carp, core module. + Exporter, core module. + + Build-Time + In addition to Run-Time: + + Test::More 0.47 or greater, core module since 5.6.2. + Test::Exception. + Test::HexString. + +SEE ALSO + Community + Official FastCGI site + + + Standards + FastCGI Specification Version 1.0 + + + RFC 3875 - The Common Gateway Interface (CGI) Version 1.1 + + + White papers + FastCGI: A High-Performance Web Server Interface + + + FastCGI - The Forgotten Treasure + + + Perl implementations + AnyEvent::FCGI + Application server implementation, built on top of AnyEvent. + Supports Responder role. Capable of multiplexing. + + FCGI + Application server implementation, built on top of "libfcgi" + (reference implementation). Supports all FastCGI roles. Responds to + management records. Processes requests synchronously. + + FCGI::Async + Application server implementation, built on top of IO::Async. + Supports Responder role. Responds to management records. Capable of + multiplexing. + + FCGI::Client + Client (Web server) implementation. Supports Responder role. + + FCGI::EV + Application server implementation, built on top of EV. Supports + Responder role. + + Mojo::Server::FastCGI + Application server implementation. Supports Responder role. + Processes requests synchronously. + + POE::Component::FastCGI + Application server implementation, built on top of POE. Supports + Responder role. Capable of multiplexing. + +SUPPORT + Please report any bugs or feature requests to + "bug-net-fastcgi@rt.cpan.org", or through the web interface at + + +AUTHOR + Christian Hansen "chansen@cpan.org" + +COPYRIGHT + Copyright 2008-2010 by Christian Hansen. + + This library is free software; you can redistribute it and/or modify it + under the same terms as Perl itself. + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/runfcgi.pl b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/runfcgi.pl new file mode 100755 index 000000000..7ee01a9f4 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/runfcgi.pl @@ -0,0 +1,226 @@ +#!/usr/bin/perl +# This program is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. +# +# (C) Paul Evans, 2010 -- leonerd@leonerd.org.uk + +use strict; +use warnings; + +use Getopt::Long; + +use Net::FastCGI::IO qw( read_record ); +use Net::FastCGI::Constant qw( :common :type :role ); +use Net::FastCGI::Protocol qw( + build_begin_request_body + build_params + parse_end_request_body +); + +sub write_record +{ + Net::FastCGI::IO::write_record(@_) or + die "Cannot write_record - $!"; +} + +my %env = ( + REQUEST_METHOD => "GET", + SCRIPT_NAME => "", + SERVER_NAME => "server", + SERVER_PORT => 80, + SERVER_PROTOCOL => "HTTP/1.1", +); + +my $stdin_from; +my $filter_stdout; + +sub usage +{ + print <<"EOF"; +$0 [options] CONNECT URL + +Runs the FastCGI found at CONNECT, as if it had received the URL + +CONNECT may be any of + + exec:PATH Execute as a child process with socket on STDIN + unix:PATH Find a UNIX socket on the given path + tcp:HOST:PORT Connect to the given port on the given host + HOST:PORT as above + +options may be: + + --body Print just the HTTP response body + --no-body Print just the HTTP response headers without the body + -m, --method METHOD Use the specified method (default "GET") + -p, --post Method is POST, pass STDIN + --put Method is PUT, pass STDIN + --stdin PATH Read STDIN from specified path, "-" means real script + +EOF +} + +GetOptions( + 'body' => sub { + defined $filter_stdout and die "Cannot --no-body and --body\n"; + $filter_stdout = "body"; + }, + 'no-body' => sub { + defined $filter_stdout and die "Cannot --no-body and --body\n"; + $filter_stdout = "headers"; + }, + 'm|method=s' => \$env{REQUEST_METHOD}, + 'p|post' => sub { + $env{REQUEST_METHOD} = "POST"; + $stdin_from = "-"; + }, + 'put' => sub { + $env{REQUEST_METHOD} = "PUT"; + $stdin_from = "-"; + }, + 'stdin=s' => \$stdin_from, + 'help' => sub { usage; exit(0) }, +) or exit(1); + +my $connect = shift @ARGV or + die "Require connection string\n"; + +my $url = shift @ARGV or + die "Require a URL"; + +if( $url =~ s{^http(s?)://([^/:]+)(?::([^/]+))?}{} ) { + $env{HTTPS} = "on" if $1; + $env{SERVER_NAME} = $2; + $env{SERVER_PORT} = $3 || ( $1 ? 443 : 80 ); +} + +$env{REQUEST_URI} = $url; + +my ( $path, $query ) = $url =~ m/^(.*)(?:\?(.*))$/; + +$env{PATH_INFO} = $path; +$env{QUERY_STRING} = $query; + +my $socket; + +if( $connect =~ m/^unix:(.*)$/ ) { + my $path = $1; + + require IO::Socket::UNIX; + + $socket = IO::Socket::UNIX->new( + Peer => $path, + ) or die "Cannot connect - $!\n"; +} +elsif( $connect =~ m/^exec:(.*)$/ ) { + my $script = $1; + + require IO::Socket::INET; + + my $listener = IO::Socket::INET->new( + LocalHost => "localhost", + Listen => 1, + ) or die "Cannot listen - $@"; + + defined( my $kid = fork ) or die "Cannot fork - $!"; + END { defined $kid and kill TERM => $kid } + + if( $kid == 0 ) { + close STDIN; + open STDIN, "<&", $listener or die "Cannot dup $listener to STDIN - $!"; + + close $listener; + + exec { $script } $script or die "Cannot exec $script - $!"; + } + + $socket = IO::Socket::INET->new( + PeerHost => $listener->sockhost, + PeerPort => $listener->sockport, + ) or die "Cannot connect - $@"; + + close $listener; +} +elsif( $connect =~ m/^(?:tcp:)?(.*):(.+?)$/ ) { + my $host = $1 || "localhost"; + my $port = $2; + + my $class = eval { require IO::Socket::IP and "IO::Socket::IP" } || + do { require IO::Socket::INET and "IO::Socket::INET" }; + + $socket = $class->new( + PeerHost => $host, + PeerPort => $port, + ) or die "Cannot connect - $@\n"; +} +else { + die "Cannot recognise connection string '$connect'\n"; +} + +write_record( $socket, FCGI_BEGIN_REQUEST, 1, + build_begin_request_body( FCGI_RESPONDER, 0 ) ); + +write_record( $socket, FCGI_PARAMS, 1, + build_params( \%env ) ); + +write_record( $socket, FCGI_PARAMS, 1, "" ); + +if( defined $stdin_from ) { + my $stdin; + + if( $stdin_from eq "-" ) { + $stdin = \*STDIN; + } + else { + open $stdin, "<", $stdin_from or die "Cannot open $stdin_from for input - $!"; + } + + while( read( $stdin, my $buffer, 8192 ) ) { + write_record( $socket, FCGI_STDIN, 1, $buffer ); + } +} + +write_record( $socket, FCGI_STDIN, 1, "" ); + +my $stdout = ""; + +while(1) { + my ( $type, $id, $content ) = read_record( $socket ) + or $! and die "Cannot read_record - $!" + or last; + + if( $type == FCGI_STDOUT ) { + if( !defined $filter_stdout ) { + print STDOUT $content; + } + elsif( $filter_stdout eq "headers" ) { + my $oldlen = length $stdout; + $stdout .= $content; + if( $stdout =~ m/\r\n\r\n/ ) { + # Print only the bit we haven't done yet + print STDOUT substr( $stdout, $oldlen, $+[0] - $oldlen ); + $filter_stdout = 1; # I.e. suppress the lot + } + else { + print STDOUT $content; + } + } + elsif( $filter_stdout eq "body" ) { + $stdout .= $content; + if( $stdout =~ m/\r\n\r\n/ ) { + print STDOUT substr( $stdout, $+[0] ); + $filter_stdout = undef; + } + } + } + elsif( $type == FCGI_STDERR ) { + print STDERR $content; + } + elsif( $type == FCGI_END_REQUEST ) { + my ( $app_status, $protocol_status ) = parse_end_request_body( $content ); + exit $app_status; + } + else { + die "Unrecognised FastCGI request type $type\n"; + } +} diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/server.pl b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/server.pl new file mode 100755 index 000000000..74a8db7e1 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/eg/server.pl @@ -0,0 +1,164 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use IO::Socket qw[]; +use PerlIO::scalar qw[]; +use Net::FastCGI::Constant qw[:type :role :flag :protocol_status FCGI_NULL_REQUEST_ID]; +use Net::FastCGI::IO qw[read_record write_record write_stream]; +use Net::FastCGI::Protocol qw[build_end_request_body + build_unknown_type_body + build_params + parse_begin_request_body + parse_params + dump_record_body ]; + +my %FCGI_VALUES = ( + FCGI_MAX_CONNS => 1, # maximum number of concurrent transport connections this application will accept + FCGI_MAX_REQS => 1, # maximum number of concurrent requests this application will accept + FCGI_MPXS_CONNS => 0, # multiplex +); + +sub handle_connection { + my ($socket, $on_request) = @_; + + my ( $current_id, # id of the request we are currently processing + $stdin, # buffer for stdin + $stdout, # buffer for stdout + $stderr, # buffer for stderr + $params, # buffer for params (environ) + $keep_conn ); # more requests on this connection? + + ($current_id, $stdin, $stdout, $stderr, $params) = (0, '', '', '', '', ''); + + use warnings FATAL => 'Net::FastCGI::IO'; + + while () { + my ($type, $request_id, $content) = read_record($socket) + or last; + + if ($request_id == FCGI_NULL_REQUEST_ID) { + if ($type == FCGI_GET_VALUES) { + my $values = parse_params($content); + my %params = map { $_ => $FCGI_VALUES{$_} } + grep { exists $FCGI_VALUES{$_} } + keys %{$values}; + write_record($socket, FCGI_GET_VALUES_RESULT, + FCGI_NULL_REQUEST_ID, build_params(\%params)); + } + else { + write_record($socket, FCGI_UNKNOWN_TYPE, + FCGI_NULL_REQUEST_ID, build_unknown_type_body($type)); + } + } + elsif ($type == FCGI_BEGIN_REQUEST) { + my ($role, $flags) = parse_begin_request_body($content); + if ($current_id || $role != FCGI_RESPONDER) { + my $status = $current_id ? FCGI_CANT_MPX_CONN : FCGI_UNKNOWN_ROLE; + write_record($socket, FCGI_END_REQUEST, $request_id, + build_end_request_body(0, $status)); + } + else { + $current_id = $request_id; + $keep_conn = ($flags & FCGI_KEEP_CONN); + } + } + elsif ($request_id != $current_id) { + # ignore inactive requests (FastCGI Specification 3.3) + } + elsif ($type == FCGI_ABORT_REQUEST) { + $current_id = 0; + ($stdin, $stdout, $stderr, $params) = ('', '', '', ''); + } + elsif ($type == FCGI_PARAMS) { + $params .= $content; + } + elsif ($type == FCGI_STDIN) { + $stdin .= $content; + + unless (length $content) { + # process request + + open(my $in, '<', \$stdin) + || die(qq/Couldn't open scalar as a file handle: $!/); + + open(my $out, '>', \$stdout) + || die(qq/Couldn't open scalar as a file handle: $!/); + + open(my $err, '>', \$stderr) + || die(qq/Couldn't open scalar as a file handle: $!/); + + my $environ = parse_params($params); + + eval { + $on_request->($environ, $in, $out, $err); + }; + + if (my $e = $@) { + warn(qq/Caught an exception in request callback: '$e'/); + $stdout = "Status: 500 Internal Server Error\n\n"; + } + + write_stream($socket, FCGI_STDOUT, $current_id, $stdout, 1); + write_stream($socket, FCGI_STDERR, $current_id, $stderr, 1) + if length $stderr; + write_record($socket, FCGI_END_REQUEST, $current_id, + build_end_request_body(0, FCGI_REQUEST_COMPLETE)); + + # prepare for next request + $current_id = 0; + ($stdin, $stdout, $stderr, $params) = ('', '', '', ''); + + last unless $keep_conn; + } + } + else { + warn(q/Received an unexpected record: / . + dump_record_body($type, $request_id, $content)); + } + } + + (!$current_id) + || warn(q/Client prematurely closed connection/); +} + +sub handle_request { + my ($env, $stdin, $stdout, $stderr) = @_; + + $env->{GATEWAY_INTERFACE} ||= 'CGI/1.1'; + + local *ENV = $env; + local *STDIN = $stdin; + local *STDOUT = $stdout; + local *STDERR = $stderr; + + print "Status: 200 OK\n"; + print "Content-Type: text/plain\n\n"; + print map { sprintf "%-25s => %s\n", $_, $ENV{$_} } sort keys %ENV; +} + +my $addr = shift(@ARGV) || 'localhost:3000'; + +my $socket = IO::Socket::INET->new( + Listen => 5, + LocalAddr => $addr, + Reuse => 1, +) or die(qq/Couldn't create INET listener socket <$addr>: '$!'./); + +print STDERR "Listening for connections on <$addr>\n"; + +while () { + my $connection = $socket->accept + or last; + + eval { + handle_connection($connection, \&handle_request); + }; + + if (my $e = $@) { + warn(qq/Caught an exception in handle_connection(): '$e'/); + } + + close $connection; +} + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm new file mode 100644 index 000000000..4126b2198 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm @@ -0,0 +1,12 @@ +package Net::FastCGI; + +use strict; +use warnings; + +our $VERSION = '0.14'; + +use Net::FastCGI::Constant; +use Net::FastCGI::Protocol; + +1; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod new file mode 100644 index 000000000..65725b958 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod @@ -0,0 +1,170 @@ +=head1 NAME + +Net::FastCGI - FastCGI Toolkit + +=head1 DESCRIPTION + +This distribution aims to provide a complete API for working with the FastCGI +protocol. + +The primary goal is to provide a function oriented and object oriented API which +are not tied to a specific I/O model or framework. + +Secondary goal is to provide higher level tools/API which can be used for debugging +and interoperability testing. + +=head1 PROGRESS + +The function oriented API is considered feature complete. L +provides functions to build and parse all FastCGI v1.0 messages, also provided is a +few convenient higher level functions such as C, +C, C and C. + +Work has begun on object oriented implementation and a simple blocking I/O class which is +intended for testing and debugging. + +=head1 PACKAGES + +=over 4 + +=item L + +FastCGI protocol constants. + +=item L + +Provides functions to read and write FastCGI messages. + +=item L + +Provides functions to build and parse FastCGI messages. + +=back + +=head1 ENVIRONMENT + +Environment variable C can be set to a true value before loading +this package to disable usage of XS implementation. + +=head1 PREREQUISITES + +=head2 Run-Time + +=over 4 + +=item L 5.6 or greater. + +=item L, core module. + +=item L, core module. + +=back + +=head2 Build-Time + +In addition to Run-Time: + +=over 4 + +=item L 0.47 or greater, core module since 5.6.2. + +=item L. + +=item L. + +=back + +=head1 SEE ALSO + +=head2 Community + +=over 4 + +=item Official FastCGI site + +L + +=back + +=head2 Standards + +=over 4 + +=item FastCGI Specification Version 1.0 + +L + +=item RFC 3875 - The Common Gateway Interface (CGI) Version 1.1 + +L + +=back + +=head2 White papers + +=over 4 + +=item FastCGI: A High-Performance Web Server Interface + +L + +=item FastCGI - The Forgotten Treasure + +L + +=back + +=head2 Perl implementations + +=over 4 + +=item L + +Application server implementation, built on top of L. Supports Responder role. +Capable of multiplexing. + +=item L + +Application server implementation, built on top of C (reference implementation). +Supports all FastCGI roles. Responds to management records. Processes requests synchronously. + +=item L + +Application server implementation, built on top of L. Supports Responder role. +Responds to management records. Capable of multiplexing. + +=item L + +Client (Web server) implementation. Supports Responder role. + +=item L + +Application server implementation, built on top of L. Supports Responder role. + +=item L + +Application server implementation. Supports Responder role. Processes requests synchronously. + +=item L + +Application server implementation, built on top of L. Supports Responder role. +Capable of multiplexing. + +=back + +=head1 SUPPORT + +Please report any bugs or feature requests to C, or through +the web interface at L + +=head1 AUTHOR + +Christian Hansen C + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pm b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pm new file mode 100644 index 000000000..1e86dbf1b --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pm @@ -0,0 +1,179 @@ +package Net::FastCGI::Constant; + +use strict; +use warnings; + +BEGIN { + our $VERSION = '0.14'; + my @common = qw[ FCGI_MAX_CONTENT_LEN + FCGI_MAX_LEN + FCGI_HEADER_LEN + FCGI_VERSION_1 + FCGI_NULL_REQUEST_ID ]; + + my @type = qw[ FCGI_BEGIN_REQUEST + FCGI_ABORT_REQUEST + FCGI_END_REQUEST + FCGI_PARAMS + FCGI_STDIN + FCGI_STDOUT + FCGI_STDERR + FCGI_DATA + FCGI_GET_VALUES + FCGI_GET_VALUES_RESULT + FCGI_UNKNOWN_TYPE + FCGI_MAXTYPE ]; + + my @role = qw[ FCGI_RESPONDER + FCGI_AUTHORIZER + FCGI_FILTER ]; + + my @flag = qw[ FCGI_KEEP_CONN ]; + + my @protocol_status = qw[ FCGI_REQUEST_COMPLETE + FCGI_CANT_MPX_CONN + FCGI_OVERLOADED + FCGI_UNKNOWN_ROLE ]; + + my @value = qw[ FCGI_MAX_CONNS + FCGI_MAX_REQS + FCGI_MPXS_CONNS ]; + + my @pack = qw[ FCGI_Header + FCGI_BeginRequestBody + FCGI_EndRequestBody + FCGI_UnknownTypeBody ]; + + my @name = qw[ @FCGI_TYPE_NAME + @FCGI_RECORD_NAME + @FCGI_ROLE_NAME + @FCGI_PROTOCOL_STATUS_NAME ]; + + our @EXPORT_OK = ( @common, + @type, + @role, + @flag, + @protocol_status, + @value, + @pack, + @name ); + + our %EXPORT_TAGS = ( all => \@EXPORT_OK, + common => \@common, + type => \@type, + role => \@role, + flag => \@flag, + protocol_status => \@protocol_status, + value => \@value, + pack => \@pack ); + + our @FCGI_TYPE_NAME = ( + undef, # 0 + 'FCGI_BEGIN_REQUEST', # 1 + 'FCGI_ABORT_REQUEST', # 2 + 'FCGI_END_REQUEST', # 3 + 'FCGI_PARAMS', # 4 + 'FCGI_STDIN', # 5 + 'FCGI_STDOUT', # 6 + 'FCGI_STDERR', # 7 + 'FCGI_DATA', # 8 + 'FCGI_GET_VALUES', # 9 + 'FCGI_GET_VALUES_RESULT', # 10 + 'FCGI_UNKNOWN_TYPE' # 11 + ); + + our @FCGI_RECORD_NAME = ( + undef, # 0 + 'FCGI_BeginRequestRecord', # 1 + 'FCGI_AbortRequestRecord', # 2 + 'FCGI_EndRequestRecord', # 3 + 'FCGI_ParamsRecord', # 4 + 'FCGI_StdinRecord', # 5 + 'FCGI_StdoutRecord', # 6 + 'FCGI_StderrRecord', # 7 + 'FCGI_DataRecord', # 8 + 'FCGI_GetValuesRecord', # 9 + 'FCGI_GetValuesResultRecord', # 10 + 'FCGI_UnknownTypeRecord', # 11 + ); + + our @FCGI_ROLE_NAME = ( + undef, # 0 + 'FCGI_RESPONDER', # 1 + 'FCGI_AUTHORIZER', # 2 + 'FCGI_FILTER', # 3 + ); + + our @FCGI_PROTOCOL_STATUS_NAME = ( + 'FCGI_REQUEST_COMPLETE', # 0 + 'FCGI_CANT_MPX_CONN', # 1 + 'FCGI_OVERLOADED', # 2 + 'FCGI_UNKNOWN_ROLE', # 3 + ); + + if (Internals->can('SvREADONLY')) { # 5.8 + Internals::SvREADONLY(@FCGI_TYPE_NAME, 1); + Internals::SvREADONLY(@FCGI_RECORD_NAME, 1); + Internals::SvREADONLY(@FCGI_ROLE_NAME, 1); + Internals::SvREADONLY(@FCGI_PROTOCOL_STATUS_NAME, 1); + Internals::SvREADONLY($_, 1) for @FCGI_TYPE_NAME, + @FCGI_RECORD_NAME, + @FCGI_ROLE_NAME, + @FCGI_PROTOCOL_STATUS_NAME; + } + + require Exporter; + *import = \&Exporter::import; +} + + +sub FCGI_LISTENSOCK_FILENO () { 0 } + +# common +sub FCGI_MAX_CONTENT_LEN () { 0xFFFF } +sub FCGI_MAX_LEN () { 0xFFFF } # deprecated +sub FCGI_HEADER_LEN () { 8 } +sub FCGI_VERSION_1 () { 1 } +sub FCGI_NULL_REQUEST_ID () { 0 } + +# type +sub FCGI_BEGIN_REQUEST () { 1 } +sub FCGI_ABORT_REQUEST () { 2 } +sub FCGI_END_REQUEST () { 3 } +sub FCGI_PARAMS () { 4 } +sub FCGI_STDIN () { 5 } +sub FCGI_STDOUT () { 6 } +sub FCGI_STDERR () { 7 } +sub FCGI_DATA () { 8 } +sub FCGI_GET_VALUES () { 9 } +sub FCGI_GET_VALUES_RESULT () { 10 } +sub FCGI_UNKNOWN_TYPE () { 11 } +sub FCGI_MAXTYPE () { FCGI_UNKNOWN_TYPE } + +# role +sub FCGI_RESPONDER () { 1 } +sub FCGI_AUTHORIZER () { 2 } +sub FCGI_FILTER () { 3 } + +# flags +sub FCGI_KEEP_CONN () { 1 } + +# protocol status +sub FCGI_REQUEST_COMPLETE () { 0 } +sub FCGI_CANT_MPX_CONN () { 1 } +sub FCGI_OVERLOADED () { 2 } +sub FCGI_UNKNOWN_ROLE () { 3 } + +# value +sub FCGI_MAX_CONNS () { 'FCGI_MAX_CONNS' } +sub FCGI_MAX_REQS () { 'FCGI_MAX_REQS' } +sub FCGI_MPXS_CONNS () { 'FCGI_MPXS_CONNS' } + +# pack +sub FCGI_Header () { 'CCnnCx' } +sub FCGI_BeginRequestBody () { 'nCx5' } +sub FCGI_EndRequestBody () { 'NCx3' } +sub FCGI_UnknownTypeBody () { 'Cx7' } + +1; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pod b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pod new file mode 100644 index 000000000..d0ca04c1f --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pod @@ -0,0 +1,264 @@ +=head1 NAME + +Net::FastCGI::Constant - FastCGI protocol constants. + +=head1 DESCRIPTION + +FastCGI protocol constants. + +=head1 CONSTANTS + +Constants can either be imported individually or in sets grouped by tag names. +The tag names are: + +=head2 C<:common> + +=over 4 + +=item C + +Maximum number of octets that the content component of the record can hold. (C<65535>) + +=item C + +Number of octets in C. (C<8>) + +=item C + +Value for C component of C. (C<1>) + +=item C + +Value for C component of C. (C<0>) + +=back + +=head2 C<:type> + +Values for C component of C. + +=over 4 + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C + +=back + +=head2 C<:flag> + +Mask for C component of C. + +=over 4 + +=item C + +=back + +=head2 C<:role> + +Values for C component of C. + +=over 4 + +=item C + +=item C + +=item C + +=back + +=head2 C<:protocol_status> + +Values for C component of C. + +=over 4 + +=item C + +=item C + +=item C + +=item C + +=back + +=head2 C<:value> + +Variable names for C / C records. + +=over 4 + +=item C + +=item C + +=item C + +=back + +=head2 C<:pack> + +C / C templates + +=over 4 + +=item C + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | Version | Type | + +-----------------+-----------------+ + 2 | Request ID | + +-----------------+-----------------+ + 4 | Content Length | + +-----------------+-----------------+ + 6 | Padding Length | Reserved | + +-----------------+-----------------+ + Total 8 octets + + Template: CCnnCx + + my ($version, $type, $request_id, $content_length, $padding_length) + = unpack(FCGI_Header, $octets); + +=item C + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | Role | + +-----------------+-----------------+ + 2 | Flags | | + +-----------------+ + + 4 | | + + Reserved + + 6 | | + +-----------------+-----------------+ + Total 8 octets + + Template: nCx5 + + my ($role, $flags) = unpack(FCGI_BeginRequestBody, $octets); + +=item C + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | | + + Application Status + + 2 | | + +-----------------+-----------------+ + 4 | Protocol Status | | + +-----------------+ Reserved + + 6 | | + +-----------------+-----------------+ + Total 8 octets + + Template: NCx3 + + my ($app_status, $protocol_status) + = unpack(FCGI_EndRequestBody, $octets); + +=item C + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | Unknown Type | | + +-----------------+ + + 2 | | + + + + 4 | Reserved | + + + + 6 | | + +-----------------+-----------------+ + Total 8 octets + + Template: Cx7 + + my $unknown_type = unpack(FCGI_UnknownTypeBody, $octets); + +=back + +=head2 C<:name> + +Arrays containing names of value components. These are read-only. + +=over 4 + +=item C<@FCGI_TYPE_NAME> + + print $FCGI_TYPE_NAME[FCGI_BEGIN_REQUEST]; # FCGI_BEGIN_REQUEST + +=item C<@FCGI_ROLE_NAME> + + print $FCGI_ROLE_NAME[FCGI_RESPONDER]; # FCGI_RESPONDER + +=item C<@FCGI_PROTOCOL_STATUS_NAME> + + print $FCGI_PROTOCOL_STATUS_NAME[FCGI_OVERLOADED]; # FCGI_OVERLOADED + +=back + +I + +It's not safe to assume that C works for validation purposes, index C<0> +might be C. + +Use boolean context instead: + + ($FCGI_TYPE_NAME[$type]) + || die; + +=head1 EXPORTS + +None by default. All functions can be exported using the C<:all> tag or individually. + +=head1 SEE ALSO + +=over 4 + +=item L + +=back + +=head1 AUTHOR + +Christian Hansen C + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm new file mode 100644 index 000000000..15583fb54 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm @@ -0,0 +1,227 @@ +package Net::FastCGI::IO; +use strict; +use warnings; +use warnings::register; + +use Carp qw[]; +use Errno qw[EBADF EINTR EPIPE]; +use Net::FastCGI::Constant qw[FCGI_HEADER_LEN]; +use Net::FastCGI::Protocol qw[build_header build_record build_stream + parse_header parse_record]; + +BEGIN { + our $VERSION = '0.14'; + our @EXPORT_OK = qw[ can_read + can_write + read_header + read_record + write_header + write_record + write_stream ]; + + our %EXPORT_TAGS = ( all => \@EXPORT_OK ); + + require Exporter; + *import = \&Exporter::import; + + eval q; +} + +*throw = \&Carp::croak; + +sub read_header { + @_ == 1 || throw(q/Usage: read_header(fh)/); + my ($fh) = @_; + + my $len = FCGI_HEADER_LEN; + my $off = 0; + my $buf; + + while ($len) { + my $r = sysread($fh, $buf, $len, $off); + if (defined $r) { + last unless $r; + $len -= $r; + $off += $r; + } + elsif ($! != EINTR) { + warnings::warn(qq) + if warnings::enabled; + return; + } + } + if ($len) { + $! = $off ? EPIPE : 0; + warnings::warn(q) + if $off && warnings::enabled; + return; + } + return parse_header($buf); +} + +sub write_header { + @_ == 5 || throw(q/Usage: write_header(fh, type, request_id, content_length, padding_length)/); + my $fh = shift; + + my $buf = &build_header; + my $len = FCGI_HEADER_LEN; + my $off = 0; + + while () { + my $r = syswrite($fh, $buf, $len, $off); + if (defined $r) { + $len -= $r; + $off += $r; + last unless $len; + } + elsif ($! != EINTR) { + warnings::warn(qq) + if warnings::enabled; + return undef; + } + } + return $off; +} + +sub read_record { + @_ == 1 || throw(q/Usage: read_record(fh)/); + my ($fh) = @_; + + my $len = FCGI_HEADER_LEN; + my $off = 0; + my $buf; + + while ($len) { + my $r = sysread($fh, $buf, $len, $off); + if (defined $r) { + last unless $r; + $len -= $r; + $off += $r; + if (!$len && $off == FCGI_HEADER_LEN) { + $len = vec($buf, 2, 16) # Content Length + + vec($buf, 6, 8); # Padding Length + } + } + elsif ($! != EINTR) { + warnings::warn(qq) + if warnings::enabled; + return; + } + } + if ($len) { + $! = $off ? EPIPE : 0; + warnings::warn(q) + if $off && warnings::enabled; + return; + } + return parse_record($buf); +} + +sub write_record { + @_ == 4 || @_ == 5 || throw(q/Usage: write_record(fh, type, request_id [, content])/); + my $fh = shift; + + my $buf = &build_record; + my $len = length $buf; + my $off = 0; + + while () { + my $r = syswrite($fh, $buf, $len, $off); + if (defined $r) { + $len -= $r; + $off += $r; + last unless $len; + } + elsif ($! != EINTR) { + warnings::warn(qq) + if warnings::enabled; + return undef; + } + } + return $off; +} + +sub write_stream { + @_ == 4 || @_ == 5 || throw(q/Usage: write_stream(fh, type, request_id, content [, terminate])/); + my $fh = shift; + + my $buf = &build_stream; + my $len = length $buf; + my $off = 0; + + while () { + my $r = syswrite($fh, $buf, $len, $off); + if (defined $r) { + $len -= $r; + $off += $r; + last unless $len; + } + elsif ($! != EINTR) { + warnings::warn(qq) + if warnings::enabled; + return undef; + } + } + return $off; +} + +sub can_read (*$) { + @_ == 2 || throw(q/Usage: can_read(fh, timeout)/); + my ($fh, $timeout) = @_; + + my $fd = fileno($fh); + unless (defined $fd && $fd >= 0) { + $! = EBADF; + return undef; + } + + my $initial = time; + my $pending = $timeout; + my $nfound; + + vec(my $fdset = '', $fd, 1) = 1; + + while () { + $nfound = select($fdset, undef, undef, $pending); + if ($nfound == -1) { + return undef unless $! == EINTR; + redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; + $nfound = 0; + } + last; + } + $! = 0; + return $nfound; +} + +sub can_write (*$) { + @_ == 2 || throw(q/Usage: can_write(fh, timeout)/); + my ($fh, $timeout) = @_; + + my $fd = fileno($fh); + unless (defined $fd && $fd >= 0) { + $! = EBADF; + return undef; + } + + my $initial = time; + my $pending = $timeout; + my $nfound; + + vec(my $fdset = '', $fd, 1) = 1; + + while () { + $nfound = select(undef, $fdset, undef, $pending); + if ($nfound == -1) { + return undef unless $! == EINTR; + redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; + $nfound = 0; + } + last; + } + $! = 0; + return $nfound; +} + +1; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod new file mode 100644 index 000000000..84a9f0973 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod @@ -0,0 +1,391 @@ +=head1 NAME + +Net::FastCGI::IO - Provides functions to read and write FastCGI messages. + +=head1 SYNOPSIS + + # FCGI_Header + @values = read_header($fh); + $header = read_header($fh); + $result = write_header($fh, $type, $request_id, $content_length, $padding_length); + + # FCGI_Record + @values = read_record($fh); + $record = read_record($fh); + $result = write_record($fh, $type, $request_id); + $result = write_record($fh, $type, $request_id, $content); + + # FCGI_Record Stream + $result = write_stream($fh, $type, $request_id, $content); + $result = write_stream($fh, $type, $request_id, $content, $terminate); + + # I/O ready + $result = can_read($fh, $timeout); + $result = can_write($fh, $timeout); + +=head1 DESCRIPTION + +Provides unbuffered blocking I/O functions to read and write FastCGI messages. + +=head1 FUNCTIONS + +=head2 read_header + +Attempts to read a C from file handle C<$fh>. + +I + + ($type, $request_id, $content_length, $padding_length) + = read_header($fh); + + $header = read_header($fh); + say $header->{type}; + say $header->{request_id}; + say $header->{content_length}; + say $header->{padding_length}; + +I + +=over 4 + +=item C<$fh> + +The file handle to read from. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=back + +I + +Upon successful completion, the return value of L. +Otherwise, a false value (C in scalar context and an empty list in list context). + +If C reaches end-of-file before reading any octets, it returns a +false value. If unsuccessful, C returns a false value and C<$!> +contains the error from the C call. If C encounters +end-of-file after some but not all of the needed octets, the function returns +a false value and sets C<$!> to C. + +I + +The implementation calls C in a loop, restarting if C +returns C with C<$!> set to C. If C does not provide +all the requested octets, C continues to call C until +either all the octets have been read, reaches end-of-file or an error occurs. + +=head2 read_record + +Attempts to read a C from file handle C<$fh>. + +I + + ($type, $request_id, $content) + = read_record($fh); + + $record = read_record($fh); + say $record->{type}; + say $record->{request_id}; + +I + +=over 4 + +=item C<$fh> + +The file handle to read from. Must be a file handle with a file descriptor. +File handle mode should be set to binary. + +=back + +I + +Upon successful completion, the return value of L. +Otherwise, a false value (C in scalar context and an empty list in list context). + +If C reaches end-of-file before reading any octets, it returns a +false value. If unsuccessful, C returns a false value and C<$!> +contains the error from the C call. If C encounters +end-of-file after some but not all of the needed octets, the function returns +a false value and sets C<$!> to C. + +I + +The implementation calls C in a loop, restarting if C +returns C with C<$!> set to C. If C does not provide +all the requested octets, C continues to call C until +either all the octets have been read, reaches end-of-file or an error occurs. + +=head2 write_header + +Attempts to write a C to file handle C<$fh>. + +I + + $result = write_header($fh, $type, $request_id, $content_length, $padding_length); + +I + +=over 4 + +=item C<$fh> + +The file handle to write to. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content_length> + +An unsigned 16-bit integer. + +=item C<$padding_length> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$result> + +Upon successful completion, the number of octets actually written. Otherwise, +C and C<$!> contains the error from the C call. + +=back + +I + +The implementation calls C in a loop, restarting if C +returns C with C<$!> set to C. If C does not output +all the requested octets, C continues to call C until +all the octets have been written or an error occurs. + +=head2 write_record + +Attempts to write a C to file handle C<$fh>. + +I + + $result = write_record($fh, $type, $request_id); + $result = write_record($fh, $type, $request_id, $content); + +I + +=over 4 + +=item C<$fh> + +The file handle to write to. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> (optional) + +A string of octets containing the content, cannot exceed 65535 octets in length. + +=back + +I + +=over 4 + +=item C<$result> + +Upon successful completion, the number of octets actually written. Otherwise, +C and C<$!> contains the error from the C call. + +=back + +I + +The implementation calls C in a loop, restarting if C +returns C with C<$!> set to C. If C does not output +all the requested octets, C continues to call C until +all the octets have been written or an error occurs. + +=head2 write_stream + +Attempts to write a C stream to file handle C<$fh>. + +I + + $result = write_stream($fh, $type, $request_id, $content); + $result = write_stream($fh, $type, $request_id, $content, $terminate); + +I + +=over 4 + +=item C<$fh> + +The file handle to write to. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the stream content. + +=item C<$terminate> (optional) + +A boolean indicating whether or not the stream should be terminated. +Defaults to false. + +=back + +I + +=over 4 + +=item C<$result> + +Upon successful completion, the number of octets actually written. Otherwise, +C and C<$!> contains the error from the C call. + +=back + +I + +The implementation calls C in a loop, restarting if C +returns C with C<$!> set to C. If C does not output +all the requested octets, C continues to call C until +all the octets have been written or an error occurs. + +=head2 can_read + +Determines wheter or not the given file handle C<$fh> is ready for reading +within the given timeout C<$timeout>. + +I + + $result = can_read($fh, $timeout); + +I + +=over 4 + +=item C<$fh> + +The file handle to test for readiness. Must be a file handle with a file descriptor. + +=item C<$timeout> + +Maximum interval to wait. Can be set to either a non-negative numeric value or +C for infinite wait. + +=back + +I + +Upon successful completion, C<0> or C<1>. Otherwise, C and C<$!> contains +the C in a loop, restarting if C error. + +I + +The implementation calls C returns +C<-1> with C<$!> set to C and C<$timeout> has not elapsed. + +=head1 EXPORTS + +None by default. All functions can be exported using the C<:all> tag or individually. + +=head1 DIAGNOSTICS + +=over 4 + +=item B<(F)> Usage: %s + +Subroutine called with wrong number of arguments. + +=item B<(W Net::FastCGI::IO)> FastCGI: Could not read %s + +=item B<(W Net::FastCGI::IO)> FastCGI: Could not write %s + +=back + +=head1 SEE ALSO + +=over 4 + +=item FastCGI Specification Version 1.0 + +L + +=item The Common Gateway Interface (CGI) Version 1.1 + +L + +=item L + +=item L + +=back + +=head1 AUTHOR + +Christian Hansen C + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm new file mode 100644 index 000000000..0c4210e9d --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm @@ -0,0 +1,203 @@ +package Net::FastCGI::Protocol; + +use strict; +use warnings; + +use Carp qw[croak]; +use Net::FastCGI qw[]; +use Net::FastCGI::Constant qw[:type :common FCGI_KEEP_CONN]; + +BEGIN { + our $VERSION = '0.14'; + our @EXPORT_OK = qw[ build_begin_request + build_begin_request_body + build_begin_request_record + build_end_request + build_end_request_body + build_end_request_record + build_header + build_params + build_record + build_stream + build_unknown_type_body + build_unknown_type_record + check_params + parse_begin_request_body + parse_end_request_body + parse_header + parse_params + parse_record + parse_record_body + parse_unknown_type_body + get_record_length + get_type_name + get_role_name + get_protocol_status_name + is_known_type + is_management_type + is_discrete_type + is_stream_type ]; + + our %EXPORT_TAGS = ( all => \@EXPORT_OK ); + + my $use_pp = $ENV{NET_FASTCGI_PP} || $ENV{NET_FASTCGI_PROTOCOL_PP}; + + if (!$use_pp) { + eval { + require Net::FastCGI::Protocol::XS; + }; + $use_pp = !!$@; + } + + if ($use_pp) { + require Net::FastCGI::Protocol::PP; + Net::FastCGI::Protocol::PP->import(@EXPORT_OK); + } + else { + Net::FastCGI::Protocol::XS->import(@EXPORT_OK); + } + + # shared between XS and PP implementation + push @EXPORT_OK, 'dump_record', 'dump_record_body'; + + require Exporter; + *import = \&Exporter::import; +} + +our $DUMP_RECORD_MAX = 78; # undocumented +our $DUMP_RECORD_ALIGN = !!0; # undocumented + +my %ESCAPES = ( + "\a" => "\\a", + "\b" => "\\b", + "\t" => "\\t", + "\n" => "\\n", + "\f" => "\\f", + "\r" => "\\r", +); + +sub dump_record { + goto \&dump_record_body if (@_ == 2 || @_ == 3); # deprecated + @_ == 1 || croak(q/Usage: dump_record(octets)/); + + my $len = &get_record_length; + ($len && $len <= length $_[0] && vec($_[0], 0, 8) == FCGI_VERSION_1) + || return '{Malformed FCGI_Record}'; + + return dump_record_body(&parse_record); +} + +sub dump_record_body { + @_ == 2 || @_ == 3 || croak(q/Usage: dump_record_body(type, request_id [, content])/); + my ($type, $request_id) = @_; + + my $content_length = defined $_[2] ? length $_[2] : 0; + + my $max = $DUMP_RECORD_MAX > 0 ? $DUMP_RECORD_MAX : FCGI_MAX_CONTENT_LEN; + my $out = ''; + + if ( $type == FCGI_PARAMS + || $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT) { + if ($content_length == 0) { + $out = q[""]; + } + elsif (check_params($_[2])) { + my ($off, $klen, $vlen) = (0); + while ($off < $content_length) { + my $pos = $off; + for ($klen, $vlen) { + $_ = vec($_[2], $off, 8); + $_ = vec(substr($_[2], $off, 4), 0, 32) & 0x7FFF_FFFF + if $_ > 0x7F; + $off += $_ > 0x7F ? 4 : 1; + } + + my $head = substr($_[2], $pos, $off - $pos); + $head =~ s/(.)/sprintf('\\%.3o',ord($1))/egs; + $out .= $head; + + my $body = substr($_[2], $off, $klen + $vlen); + for ($body) { + s/([\\\"])/\\$1/g; + s/([\a\b\t\n\f\r])/$ESCAPES{$1}/g; + s/([^\x20-\x7E])/sprintf('\\x%.2X',ord($1))/eg; + } + $out .= $body; + $off += $klen + $vlen; + last if $off > $max; + } + substr($out, $max - 5) = ' ... ' + if length $out > $max; + $out = qq["$out"]; + } + else { + $out = 'Malformed FCGI_NameValuePair(s)'; + } + } + elsif ( $type == FCGI_BEGIN_REQUEST + || $type == FCGI_END_REQUEST + || $type == FCGI_UNKNOWN_TYPE) { + if ($content_length != 8) { + my $name = $type == FCGI_BEGIN_REQUEST ? 'FCGI_BeginRequestBody' + : $type == FCGI_END_REQUEST ? 'FCGI_EndRequestBody' + : 'FCGI_UnknownTypeBody'; + $out = sprintf '{Malformed %s (expected 8 octets got %d)}', $name, $content_length; + } + elsif ($type == FCGI_BEGIN_REQUEST) { + my ($role, $flags) = parse_begin_request_body($_[2]); + if ($flags != 0) { + my @set; + if ($flags & FCGI_KEEP_CONN) { + $flags &= ~FCGI_KEEP_CONN; + push @set, 'FCGI_KEEP_CONN'; + } + if ($flags) { + push @set, sprintf '0x%.2X', $flags; + } + $flags = join '|', @set; + } + $out = sprintf '{%s, %s}', get_role_name($role), $flags; + } + elsif($type == FCGI_END_REQUEST) { + my ($astatus, $pstatus) = parse_end_request_body($_[2]); + $out = sprintf '{%d, %s}', $astatus, get_protocol_status_name($pstatus); + } + else { + my $unknown_type = parse_unknown_type_body($_[2]); + $out = sprintf '{%s}', get_type_name($unknown_type); + } + } + elsif ($content_length) { + my $looks_like_binary = do { + my $count = () = $_[2] =~ /[\r\n\t\x20-\x7E]/g; + ($count / $content_length) < 0.7; + }; + $out = substr($_[2], 0, $max + 1); + for ($out) { + if ($looks_like_binary) { + s/(.)/sprintf('\\x%.2X',ord($1))/egs; + } + else { + s/([\\\"])/\\$1/g; + s/([\a\b\t\n\f\r])/$ESCAPES{$1}/g; + s/([^\x20-\x7E])/sprintf('\\x%.2X',ord($1))/eg; + } + } + substr($out, $max - 5) = ' ... ' + if length $out > $max; + $out = qq["$out"]; + } + else { + $out = q[""]; + } + + my $name = get_type_name($type); + my $width = 0; + $width = 27 - length $name # length("FCGI_GET_VALUES_RESULT") == 22 + if $DUMP_RECORD_ALIGN; # + length(0xFFFF) == 5 + return sprintf '{%s, %*d, %s}', $name, $width, $request_id, $out; +} + +1; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod new file mode 100644 index 000000000..64f6a7e66 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod @@ -0,0 +1,1227 @@ +=head1 NAME + +Net::FastCGI::Protocol - Provides functions to build and parse FastCGI messages. + +=head1 SYNOPSIS + + # FCGI_Header + $octets = build_header($type, $request_id, $content_length, $padding_length); + @values = parse_header($octets); + $header = parse_header($octets); + + # FCGI_BeginRequestBody + $octets = build_begin_request_body($role, $flags); + @values = parse_begin_request_body($octets); + + # FCGI_EndRequestBody + $octets = build_end_request_body($app_status, $protocol_status); + @values = parse_end_request_body($octets); + + # FCGI_UnknownTypeBody + $octets = build_unknown_type_body($type); + @values = parse_unknown_type_body($octets); + + # FCGI_BeginRequestRecord + $octets = build_begin_request_record($request_id, $role, $flags); + + # FCGI_EndRequestRecord + $octets = build_end_request_record($request_id, $app_status, $protocol_status); + + # FCGI_UnknownTypeRecord + $octets = build_unknown_type_record($type); + + # FCGI_NameValuePair's + $octets = build_params($params); + $params = parse_params($octets); + $bool = check_params($octets); + + # FCGI_Record + $octets = build_record($type, $request_id); + $octets = build_record($type, $request_id, $content); + @values = parse_record($octets); + $record = parse_record($octets); + $record = parse_record_body($type, $request_id, $content); + + # FCGI_Record Debugging / Tracing + $string = dump_record($octets); + $string = dump_record_body($type, $request_id, $content); + + # FCGI_Record Stream + $octets = build_stream($type, $request_id, $content); + $octets = build_stream($type, $request_id, $content, $terminate); + + # Begin Request + $octets = build_begin_request($request_id, $role, $flags, $params); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin, $data); + + # End Request + $octets = build_end_request($request_id, $app_status, $protocol_status); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout, $stderr); + +=head1 DESCRIPTION + +Provides functions to build and parse FastCGI messages. + +=head1 FUNCTIONS + +Please note that all functions in this package expects octets, not unicode strings. +It's the callers responsibility to ensure this. If any of theese functions is called +with unicode strings containing code points above 255, they will most likely produce +malformed messages. + +=head2 build_begin_request + +Builds a Begin Request message. + +I + + $octets = build_begin_request($request_id, $role, $flags, $params); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin, $data); + +I + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. Identifier of the request. + +=item C<$role> + +An unsigned 16-bit integer. This should be set to either C, +C or C. + +=item C<$flags> + +An unsigned 8-bit integer. This should be set to either C<0> or contain the +mask C if a persistent connection is desired. + +=item C<$params> + +A hash reference containing name-value pairs. This is the CGI environ that the +application expects. + +=item C<$stdin> (optional) + +A string of octets containing the C content. This should only be +provided if C<$role> is set to either C or C. The +C stream is terminated if provided. + +=item C<$data> (optional) + +A string of octets containing the C content. This should only be +provided if C<$role> is set to C. The C stream is +terminated if provided. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the message. + +=back + +=head2 build_begin_request_body + +Builds a C. + +I + + $octets = build_begin_request_body($role, $flags); + +I + +=over 4 + +=item C<$role> + +An unsigned 16-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the body. String is 8 octets in length. + +=back + +=head2 build_begin_request_record + +Builds a C. + +I + + $octets = build_begin_request_record($request_id, $role, $flags); + +I + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$role> + +An unsigned 16-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the record. String is 16 octets in length. + +=back + +=head2 build_end_request + +Builds a End Request message + +I + + $octets = build_end_request($request_id, $app_status, $protocol_status); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout, $stderr); + +I + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. Identifier of the request. + +=item C<$app_status> + +An unsigned 32-bit integer. Application status code of the request. + +=item C<$protocol_status> + +An unsigned 8-bit integer. This should be set to either C, +C, C or C. + +=item C<$stdout> (optional) + +A string of octets containing the C content. The C +stream is terminated if provided. + +=item C<$stderr> (optional) + +A string of octets containing the C content. The C +stream is terminated if provided. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the message. + +=back + +I + +This function is equivalent to C if called without +C<$stdout> and C<$stderr>. + +=head2 build_end_request_body + +Builds a C. + +I + + $octets = build_end_request_body($app_status, $protocol_status); + +I + +=over 4 + +=item C<$app_status> + +An unsigned 32-bit integer. + +=item C<$protocol_status> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the body. String is 8 octets in length. + +=back + +=head2 build_end_request_record + +Builds a C. + +I + + $octets = build_end_request_record($request_id, $app_status, $protocol_status); + +I + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$app_status> + +An unsigned 32-bit integer. + +=item C<$protocol_status> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the record. String is 16 octets in length. + +=back + +=head2 build_header + +Builds a C. + +I + + $octets = build_header($type, $request_id, $content_length, $padding_length); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content_length> + +An unsigned 16-bit integer. + +=item C<$padding_length> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the header. String is 8 octets in length. + +=back + +=head2 build_params + +Builds C's. + +I + + $octets = build_params($params); + +I + +=over 4 + +=item C<$params> + +A hash reference containing name-value pairs. + +=back + +I + +=over 4 + +=item C<$octets> + +=back + +=head2 build_record + +Builds a C. + +I + + $octets = build_record($type, $request_id); + $octets = build_record($type, $request_id, $content); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> (optional) + +A string of octets containing the content, cannot exceed 65535 octets in length. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the record. + +=back + +I + +Follows the recommendation in specification and pads the record by +8-(content_length mod 8) zero-octets. + +=head2 build_stream + +Builds a series of stream records. + +I + + $octets = build_stream($type, $request_id, $content); + $octets = build_stream($type, $request_id, $content, $terminate); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the stream content. + +=item C<$terminate> (optional) + +A boolean indicating whether or not the stream should be terminated. +Defaults to false. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the stream. + +=back + +I + +Stream is not terminated if C<$content> is empty unless C<$terminate> is set. + +C<$content> is split in segment sizes of 32760 octets (32768 - FCGI_HEADER_LEN). + +=head2 build_unknown_type_body + +Builds a C. + +I + + $octets = build_unknown_type_body($type); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the body. String is 8 octets in length. + +=back + +=head2 build_unknown_type_record + +Builds a C. + +I + + $octets = build_unknown_type_record($type); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the record. String is 16 octets in length. + +=back + +=head2 check_params + +Determine wheter or not params is well-formed. + +I + + $boolean = check_params($octets); + +I + +=over 4 + +=item C<$octets> + +A string of octets containing C's. + +=back + +I + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$octets> consist of well-formed C's. + +=back + +=head2 dump_record + +Dump a C. + +I + + $string = dump_record($octets); + +I + +=over 4 + +=item C<$octets> + +A string of octets containing at least one record. + +=back + +I + +=over 4 + +=item C<$string> + +A short (less than 100 characters) string representation of the record in printable US-ASCII. + +=back + +=head2 dump_record_body + +Dump a C. + +I + + $string = dump_record_body($type, $request_id); + $string = dump_record_body($type, $request_id, $content); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> (optional) + +A string of octets containing the content. + +=back + +I + +=over 4 + +=item C<$string> + +A short (less than 100 characters) string representation of the record in printable US-ASCII. + +=back + +=head2 parse_begin_request_body + +Parses a C. + +I + + ($role, $flags) = parse_begin_request_body($octets); + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the body, must be greater than or equal to 8 octets in length. + +=back + +I + +=over 4 + +=item C<$role> + +An unsigned 16-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +=head2 parse_end_request_body + +Parses a C. + +I + + ($app_status, $protocol_status) = parse_end_request_body($octets); + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the body, must be greater than or equal to 8 octets in length. + +=back + +I + +=over 4 + +=item C<$app_status> + +An unsigned 32-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +=head2 parse_header + +Parses a C. + +I + + ($type, $request_id, $content_length, $padding_length) + = parse_header($octets); + + $header = parse_header($octets); + say $header->{type}; + say $header->{request_id}; + say $header->{content_length}; + say $header->{padding_length}; + +I + +=over 4 + +=item C<$octets> + +A string of octets containing the header, must be greater than or equal to 8 octets in length. + +=back + +I + +In list context: + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content_length> + +An unsigned 16-bit integer. + +=item C<$padding_length> + +An unsigned 8-bit integer. + +=back + +In scalar context a hash reference containing above variable names as keys. + +=head2 parse_params + +Parses C's. + +I + + $params = parse_params($octets); + +I + +=over 4 + +=item C<$octets> + +A string of octets containing C's. + +=back + +I + +=over 4 + +=item C<$params> + +A hash reference containing name-value pairs. + +=back + +=head2 parse_record + +Parses a C. + +I + + ($type, $request_id, $content) + = parse_record($octets); + + $record = parse_record($octets); + say $record->{type}; + say $record->{request_id}; + +I + +=over 4 + +=item C<$octets> + +A string of octets containing at least one record. + +=back + +I + +In list context: + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the record content. + +=back + +In scalar context a hash reference containing the C components. +See L. + +=head2 parse_record_body + +Parses a C. + +I + + $record = parse_record_body($type, $request_id, $content); + say $record->{type}; + say $record->{request_id}; + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the record content. + +=back + +I + +A hash reference which represents the C. The content depends on the +type of record. All record types have the keys: C and C. + +=over 4 + +=item C + +=over 8 + +=item C + +An unsigned 16-bit integer. + +=item C + +An unsigned 8-bit integer. + +=back + +=item C + +=over 8 + +=item C + +An unsigned 32-bit integer. + +=item C + +An unsigned 8-bit integer. + +=back + +=item C + +=item C + +=item C + +=item C + +=item C + +=over 8 + +=item C + +A string of octets containing the content of the stream. + +=back + +=item C + +=item C + +=over 8 + +=item C + +A hash reference containing name-value pairs. + +=back + +=item C + +=over 8 + +=item C + +An unsigned 8-bit integer. + +=back + +=back + +=head2 parse_unknown_type_body + +Parses a C. + +I + + $type = parse_unknown_type_body($octets); + +I + +=over 4 + +=item C<$octets> + +C<$octets> must be greater than or equal to 8 octets in length. + +=back + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +=head2 get_record_length + +I + + $length = get_record_length($octets); + +I + +=over 4 + +=item C<$octets> + +A string of octets containing at least one C. + +=back + +I + +=over 4 + +=item C<$length> + +An unsigned integer containing the length of record in octets. If C<$octets> +contains insufficient octets C<(< FCGI_HEADER_LEN)> C<0> is returned. + +=back + +=head2 get_type_name + +I + + $name = get_type_name($type); + $name = get_type_name(FCGI_BEGIN_REQUEST); # 'FCGI_BEGIN_REQUEST' + $name = get_type_name(255); # '0xFF' + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$name> + +A string containing the name of the type. If C<$type> is not a known v1.0 type, +a hexadecimal value is returned. + +=back + +I + +See also L. + +=head2 get_role_name + +I + + $name = get_role_name($type); + $name = get_role_name(FCGI_RESPONDER); # 'FCGI_RESPONDER' + $name = get_role_name(65535); # '0xFFFF' + +I + +=over 4 + +=item C<$role> + +An unsigned 16-bit integer. + +=back + +I + +=over 4 + +=item C<$name> + +A string containing the name of the role. If C<$role> is not a known v1.0 role, +a hexadecimal value is returned. + +=back + +I + +See also L. + +=head2 get_protocol_status_name + +I + + $name = get_protocol_status_name($protocol_status); + $name = get_protocol_status_name(FCGI_REQUEST_COMPLETE); # 'FCGI_REQUEST_COMPLETE' + $name = get_protocol_status_name(255); # '0xFF' + +I + +=over 4 + +=item C<$protocol_status> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$name> + +A string containing the name of the protocol status. If C<$protocol_status> is +not a known v1.0 protocol status code, a hexadecimal value is returned. + +=back + +I + +See also L. + +=head2 is_known_type + +I + + $boolean = is_known_type($type); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a known FastCGI v1.0 type. + +=back + +=head2 is_management_type + +I + + $boolean = is_management_type($type); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a management type. + +=back + +=head2 is_discrete_type + +I + + $boolean = is_discrete_type($type); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a discrete type. + +=back + +=head2 is_stream_type + +I + + $boolean = is_stream_type($type); + +I + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a stream type. + +=back + +=head1 EXPORTS + +None by default. All functions can be exported using the C<:all> tag or individually. + +=head1 DIAGNOSTICS + +=over 4 + +=item B<(F)> Usage: %s + +Subroutine called with wrong number of arguments. + +=item B<(F)> Invalid Argument: %s + +=item B<(F)> FastCGI: Insufficient number of octets to parse %s + +=item B<(F)> FastCGI: Malformed record %s + +=item B<(F)> FastCGI: Protocol version mismatch (0x%.2X) + +=back + +=head1 SEE ALSO + +=over 4 + +=item FastCGI Specification Version 1.0 + +L + +=item The Common Gateway Interface (CGI) Version 1.1 + +L + +=item L + + +=back + +=head1 AUTHOR + +Christian Hansen C + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm new file mode 100644 index 000000000..bfba2579f --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm @@ -0,0 +1,429 @@ +package Net::FastCGI::Protocol::PP; +use strict; +use warnings; + +use Carp qw[]; +use Net::FastCGI::Constant qw[:all]; + +BEGIN { + our $VERSION = '0.14'; + our @EXPORT_OK = qw[ build_begin_request + build_begin_request_body + build_begin_request_record + build_end_request + build_end_request_body + build_end_request_record + build_header + build_params + build_record + build_stream + build_unknown_type_body + build_unknown_type_record + check_params + parse_begin_request_body + parse_end_request_body + parse_header + parse_params + parse_record + parse_record_body + parse_unknown_type_body + is_known_type + is_management_type + is_discrete_type + is_stream_type + get_record_length + get_role_name + get_type_name + get_protocol_status_name ]; + + our %EXPORT_TAGS = ( all => \@EXPORT_OK ); + + require Exporter; + *import = \&Exporter::import; +} + +sub TRUE () { !!1 } +sub FALSE () { !!0 } + +sub ERRMSG_OCTETS () { q/FastCGI: Insufficient number of octets to parse %s/ } +sub ERRMSG_MALFORMED () { q/FastCGI: Malformed record %s/ } +sub ERRMSG_VERSION () { q/FastCGI: Protocol version mismatch (0x%.2X)/ } +sub ERRMSG_OCTETS_LE () { q/Invalid Argument: '%s' cannot exceed %u octets in length/ } + +sub throw { + @_ = ( sprintf($_[0], @_[1..$#_]) ) if @_ > 1; + goto \&Carp::croak; +} + +# FCGI_Header + +sub build_header { + @_ == 4 || throw(q/Usage: build_header(type, request_id, content_length, padding_length)/); + return pack(FCGI_Header, FCGI_VERSION_1, @_); +} + +sub parse_header { + @_ == 1 || throw(q/Usage: parse_header(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_Header/); + (vec($_[0], 0, 8) == FCGI_VERSION_1) + || throw(ERRMSG_VERSION, unpack('C', $_[0])); + return unpack('xCnnCx', $_[0]) + if wantarray; + my %header; + @header{qw(type request_id content_length padding_length)} + = unpack('xCnnCx', $_[0]); + return \%header; +} + +# FCGI_BeginRequestBody + +sub build_begin_request_body { + @_ == 2 || throw(q/Usage: build_begin_request_body(role, flags)/); + return pack(FCGI_BeginRequestBody, @_); +} + +sub parse_begin_request_body { + @_ == 1 || throw(q/Usage: parse_begin_request_body(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_BeginRequestBody/); + return unpack(FCGI_BeginRequestBody, $_[0]); +} + +# FCGI_EndRequestBody + +sub build_end_request_body { + @_ == 2 || throw(q/Usage: build_end_request_body(app_status, protocol_status)/); + return pack(FCGI_EndRequestBody, @_); +} + +sub parse_end_request_body { + @_ == 1 || throw(q/Usage: parse_end_request_body(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_EndRequestBody/); + return unpack(FCGI_EndRequestBody, $_[0]); +} + +# FCGI_UnknownTypeBody + +sub build_unknown_type_body { + @_ == 1 || throw(q/Usage: build_unknown_type_body(type)/); + return pack(FCGI_UnknownTypeBody, @_); +} + +sub parse_unknown_type_body { + @_ == 1 || throw(q/Usage: parse_unknown_type_body(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_UnknownTypeBody/); + return unpack(FCGI_UnknownTypeBody, $_[0]); +} + +# FCGI_BeginRequestRecord + +sub build_begin_request_record { + @_ == 3 || throw(q/Usage: build_begin_request_record(request_id, role, flags)/); + my ($request_id, $role, $flags) = @_; + return build_record(FCGI_BEGIN_REQUEST, $request_id, + build_begin_request_body($role, $flags)); +} + +# FCGI_EndRequestRecord + +sub build_end_request_record { + @_ == 3 || throw(q/Usage: build_end_request_record(request_id, app_status, protocol_status)/); + my ($request_id, $app_status, $protocol_status) = @_; + return build_record(FCGI_END_REQUEST, $request_id, + build_end_request_body($app_status, $protocol_status)); +} + +# FCGI_UnknownTypeRecord + +sub build_unknown_type_record { + @_ == 1 || throw(q/Usage: build_unknown_type_record(type)/); + my ($type) = @_; + return build_record(FCGI_UNKNOWN_TYPE, FCGI_NULL_REQUEST_ID, + build_unknown_type_body($type)); +} + +sub build_record { + @_ == 2 || @_ == 3 || throw(q/Usage: build_record(type, request_id [, content])/); + my ($type, $request_id) = @_; + + my $content_length = defined $_[2] ? length $_[2] : 0; + my $padding_length = (8 - ($content_length % 8)) % 8; + + ($content_length <= FCGI_MAX_CONTENT_LEN) + || throw(ERRMSG_OCTETS_LE, q/content/, FCGI_MAX_CONTENT_LEN); + + my $res = build_header($type, $request_id, $content_length, $padding_length); + + if ($content_length) { + $res .= $_[2]; + } + + if ($padding_length) { + $res .= "\x00" x $padding_length; + } + + return $res; +} + +sub parse_record { + @_ == 1 || throw(q/Usage: parse_record(octets)/); + my ($type, $request_id, $content_length) = &parse_header; + + (length $_[0] >= FCGI_HEADER_LEN + $content_length) + || throw(ERRMSG_OCTETS, q/FCGI_Record/); + + return wantarray + ? ($type, $request_id, substr($_[0], FCGI_HEADER_LEN, $content_length)) + : parse_record_body($type, $request_id, + substr($_[0], FCGI_HEADER_LEN, $content_length)); +} + +sub parse_record_body { + @_ == 3 || throw(q/Usage: parse_record_body(type, request_id, content)/); + my ($type, $request_id) = @_; + + my $content_length = defined $_[2] ? length $_[2] : 0; + + ($content_length <= FCGI_MAX_CONTENT_LEN) + || throw(ERRMSG_OCTETS_LE, q/content/, FCGI_MAX_CONTENT_LEN); + + my %record = (type => $type, request_id => $request_id); + if ($type == FCGI_BEGIN_REQUEST) { + ($request_id != FCGI_NULL_REQUEST_ID && $content_length == 8) + || throw(ERRMSG_MALFORMED, q/FCGI_BeginRequestRecord/); + @record{ qw(role flags) } = parse_begin_request_body($_[2]); + } + elsif ($type == FCGI_ABORT_REQUEST) { + ($request_id != FCGI_NULL_REQUEST_ID && $content_length == 0) + || throw(ERRMSG_MALFORMED, q/FCGI_AbortRequestRecord/); + } + elsif ($type == FCGI_END_REQUEST) { + ($request_id != FCGI_NULL_REQUEST_ID && $content_length == 8) + || throw(ERRMSG_MALFORMED, q/FCGI_EndRequestRecord/); + @record{ qw(app_status protocol_status) } + = parse_end_request_body($_[2]); + } + elsif ( $type == FCGI_PARAMS + || $type == FCGI_STDIN + || $type == FCGI_STDOUT + || $type == FCGI_STDERR + || $type == FCGI_DATA) { + ($request_id != FCGI_NULL_REQUEST_ID) + || throw(ERRMSG_MALFORMED, $FCGI_RECORD_NAME[$type]); + $record{content} = $content_length ? $_[2] : ''; + } + elsif ( $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT) { + ($request_id == FCGI_NULL_REQUEST_ID) + || throw(ERRMSG_MALFORMED, $FCGI_RECORD_NAME[$type]); + $record{values} = parse_params($_[2]); + } + elsif ($type == FCGI_UNKNOWN_TYPE) { + ($request_id == FCGI_NULL_REQUEST_ID && $content_length == 8) + || throw(ERRMSG_MALFORMED, q/FCGI_UnknownTypeRecord/); + $record{unknown_type} = parse_unknown_type_body($_[2]); + } + else { + # unknown record type, pass content so caller can decide appropriate action + $record{content} = $_[2] if $content_length; + } + + return \%record; +} + +# Reference implementation use 8192 (libfcgi) +sub FCGI_SEGMENT_LEN () { 32768 - FCGI_HEADER_LEN } + +sub build_stream { + @_ == 3 || @_ == 4 || throw(q/Usage: build_stream(type, request_id, content [, terminate])/); + my ($type, $request_id, undef, $terminate) = @_; + + my $len = defined $_[2] ? length $_[2] : 0; + my $res = ''; + + if ($len) { + if ($len < FCGI_SEGMENT_LEN) { + $res = build_record($type, $request_id, $_[2]); + } + else { + my $header = build_header($type, $request_id, FCGI_SEGMENT_LEN, 0); + my $off = 0; + while ($len >= FCGI_SEGMENT_LEN) { + $res .= $header; + $res .= substr($_[2], $off, FCGI_SEGMENT_LEN); + $len -= FCGI_SEGMENT_LEN; + $off += FCGI_SEGMENT_LEN; + } + if ($len) { + $res .= build_record($type, $request_id, substr($_[2], $off, $len)); + } + } + } + + if ($terminate) { + $res .= build_header($type, $request_id, 0, 0); + } + + return $res; +} + +sub build_params { + @_ == 1 || throw(q/Usage: build_params(params)/); + my ($params) = @_; + my $res = ''; + while (my ($key, $val) = each(%$params)) { + for ($key, $val) { + my $len = defined $_ ? length : 0; + $res .= $len < 0x80 ? pack('C', $len) : pack('N', $len | 0x8000_0000); + } + $res .= $key; + $res .= $val if defined $val; + } + return $res; +} + +sub parse_params { + @_ == 1 || throw(q/Usage: parse_params(octets)/); + my ($octets) = @_; + + (defined $octets) + || return +{}; + + my ($params, $klen, $vlen) = ({}, 0, 0); + while (length $octets) { + for ($klen, $vlen) { + (1 <= length $octets) + || throw(ERRMSG_OCTETS, q/FCGI_NameValuePair/); + $_ = vec(substr($octets, 0, 1, ''), 0, 8); + next if $_ < 0x80; + (3 <= length $octets) + || throw(ERRMSG_OCTETS, q/FCGI_NameValuePair/); + $_ = vec(pack('C', $_ & 0x7F) . substr($octets, 0, 3, ''), 0, 32); + } + ($klen + $vlen <= length $octets) + || throw(ERRMSG_OCTETS, q/FCGI_NameValuePair/); + my $key = substr($octets, 0, $klen, ''); + $params->{$key} = substr($octets, 0, $vlen, ''); + } + return $params; +} + +sub check_params { + @_ == 1 || throw(q/Usage: check_params(octets)/); + (defined $_[0]) + || return FALSE; + + my ($len, $off, $klen, $vlen) = (length $_[0], 0, 0, 0); + while ($off < $len) { + for ($klen, $vlen) { + (($off += 1) <= $len) + || return FALSE; + $_ = vec($_[0], $off - 1, 8); + next if $_ < 0x80; + (($off += 3) <= $len) + || return FALSE; + $_ = vec(substr($_[0], $off - 4, 4), 0, 32) & 0x7FFF_FFFF; + } + (($off += $klen + $vlen) <= $len) + || return FALSE; + } + return TRUE; +} + +sub build_begin_request { + (@_ >= 4 && @_ <= 6) || throw(q/Usage: build_begin_request(request_id, role, flags, params [, stdin [, data]])/); + my ($request_id, $role, $flags, $params) = @_; + + my $r = build_begin_request_record($request_id, $role, $flags) + . build_stream(FCGI_PARAMS, $request_id, build_params($params), TRUE); + + if (@_ > 4) { + $r .= build_stream(FCGI_STDIN, $request_id, $_[4], TRUE); + if (@_ > 5) { + $r .= build_stream(FCGI_DATA, $request_id, $_[5], TRUE); + } + } + return $r; +} + +sub build_end_request { + (@_ >= 3 && @_ <= 5) || throw(q/Usage: build_end_request(request_id, app_status, protocol_status [, stdout [, stderr]])/); + my ($request_id, $app_status, $protocol_status) = @_; + + my $r; + if (@_ > 3) { + $r .= build_stream(FCGI_STDOUT, $request_id, $_[3], TRUE); + if (@_ > 4) { + $r .= build_stream(FCGI_STDERR, $request_id, $_[4], TRUE); + } + } + $r .= build_end_request_record($request_id, $app_status, $protocol_status); + return $r; +} + +sub get_record_length { + @_ == 1 || throw(q/Usage: get_record_length(octets)/); + (defined $_[0] && length $_[0] >= FCGI_HEADER_LEN) + || return 0; + return FCGI_HEADER_LEN + vec($_[0], 2, 16) # contentLength + + vec($_[0], 6, 8); # paddingLength +} + +sub is_known_type { + @_ == 1 || throw(q/Usage: is_known_type(type)/); + my ($type) = @_; + return ($type > 0 && $type <= FCGI_MAXTYPE); +} + +sub is_discrete_type { + @_ == 1 || throw(q/Usage: is_discrete_type(type)/); + my ($type) = @_; + return ( $type == FCGI_BEGIN_REQUEST + || $type == FCGI_ABORT_REQUEST + || $type == FCGI_END_REQUEST + || $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT + || $type == FCGI_UNKNOWN_TYPE ); +} + +sub is_management_type { + @_ == 1 || throw(q/Usage: is_management_type(type)/); + my ($type) = @_; + return ( $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT + || $type == FCGI_UNKNOWN_TYPE ); +} + +sub is_stream_type { + @_ == 1 || throw(q/Usage: is_stream_type(type)/); + my ($type) = @_; + return ( $type == FCGI_PARAMS + || $type == FCGI_STDIN + || $type == FCGI_STDOUT + || $type == FCGI_STDERR + || $type == FCGI_DATA ); +} + +sub get_type_name { + @_ == 1 || throw(q/Usage: get_type_name(type)/); + my ($type) = @_; + return $FCGI_TYPE_NAME[$type] || sprintf('0x%.2X', $type); +} + +sub get_role_name { + @_ == 1 || throw(q/Usage: get_role_name(role)/); + my ($role) = @_; + return $FCGI_ROLE_NAME[$role] || sprintf('0x%.4X', $role); +} + +sub get_protocol_status_name { + @_ == 1 || throw(q/Usage: get_protocol_status_name(protocol_status)/); + my ($status) = @_; + return $FCGI_PROTOCOL_STATUS_NAME[$status] || sprintf('0x%.2X', $status); +} + +1; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/000_load.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/000_load.t new file mode 100644 index 000000000..1436a558b --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/000_load.t @@ -0,0 +1,29 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 5; + +BEGIN { + use_ok('Net::FastCGI'); + use_ok('Net::FastCGI::Constant'); + use_ok('Net::FastCGI::IO'); + use_ok('Net::FastCGI::Protocol'); + + if ( $ENV{NET_FASTCGI_PP} ) { + use_ok('Net::FastCGI::Protocol::PP'); + } + else { + use_ok('Net::FastCGI::Protocol::XS'); + } +} + +diag("Net::FastCGI $Net::FastCGI::VERSION, Perl $], $^X"); +diag("NET_FASTCGI_PP=$ENV{NET_FASTCGI_PP}"); + + + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/001_header.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/001_header.t new file mode 100644 index 000000000..8e1476b5e --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/001_header.t @@ -0,0 +1,51 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 13; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_header + parse_header ]); +} + +my @tests = ( + # octets type request_id content_length padding_length + ["\x01\x00\x00\x00\x00\x00\x00\x00", 0, 0, 0, 0 ], + ["\x01\xFF\xFF\xFF\xFF\xFF\xFF\x00", 0xFF, 0xFFFF, 0xFFFF, 0xFF ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_header(@$test[1..4]); + is_hexstr($got, $expected, 'build_header()'); +} + +foreach my $test (@tests) { + my @expected = @$test[1..4]; + my @got = parse_header($test->[0]); + is_deeply(\@got, \@expected, "parse_header() in list context"); +} + +my @components = qw(type request_id content_length padding_length); +foreach my $test (@tests) { + my $expected; @$expected{@components} = @$test[1..4]; + my $got = parse_header($test->[0]); + is_deeply($got, $expected, "parse_header() in scalar context"); +} + + +throws_ok { parse_header("") } qr/FastCGI: Insufficient .* FCGI_Header/; +throws_ok { parse_header(undef) } qr/FastCGI: Insufficient .* FCGI_Header/; +throws_ok { parse_header("\x00\x00\x00\x00\x00\x00\x00\x00") } qr/^FastCGI: Protocol version mismatch/; +throws_ok { parse_header("\xFF\x00\x00\x00\x00\x00\x00\x00") } qr/^FastCGI: Protocol version mismatch/; + +throws_ok { build_header() } qr/^Usage: /; +throws_ok { parse_header() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/005_record_length.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/005_record_length.t new file mode 100644 index 000000000..1d2381980 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/005_record_length.t @@ -0,0 +1,44 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 18; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Constant', qw[:all]); + use_ok('Net::FastCGI::Protocol', qw[ build_header + build_record + get_record_length ]); +} + + +is get_record_length(undef), 0, 'get_record_length(undef)'; + +{ + for my $len (0..7) { + is get_record_length("\x00" x $len), 0, qq; + } +} + +{ + for my $len (8, 16, 32, 64) { + my $record = build_record(0, 0, "\x00" x $len); + is get_record_length($record), FCGI_HEADER_LEN + $len; + } +} + +{ + my $header = build_header(0, 0, 8192, 250); + is get_record_length($header), FCGI_HEADER_LEN + 8192 + 250; +} + +# get_record_length(octets) +for (0, 2) { + throws_ok { get_record_length((1) x $_) } qr/^Usage: /; +} + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/010_build_record.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/010_build_record.t new file mode 100644 index 000000000..9f321261c --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/010_build_record.t @@ -0,0 +1,44 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 11; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_record ]); +} + +my @tests = ( + # octets type request_id content + [ "\x01\x00\x00\x00\x00\x00\x00\x00", 0, 0, undef ], + [ "\x01\xFF\xFF\xFF\x00\x00\x00\x00", 0xFF, 0xFFFF, undef ], + [ "\x01\x01\x00\x01\x00\x01\x07\x00\x01\x00\x00\x00\x00\x00\x00\x00", 1, 1, "\x01" ], + [ "\x01\x01\x00\x01\x00\x05\x03\x00\x01\x01\x01\x01\x01\x00\x00\x00", 1, 1, "\x01\x01\x01\x01\x01" ], + [ "\x01\x01\x00\x01\x00\x08\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01", 1, 1, "\x01\x01\x01\x01\x01\x01\x01\x01" ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_record(@$test[1..3]); + is_hexstr($got, $expected, 'build_record()'); +} + +{ + my $exp = "\x01\x01\x00\x02\x00\x00\x00\x00"; + my $got = build_record(1, 2); + is_hexstr($got, $exp, 'build_record(1, 2)'); +} + +throws_ok { build_record( 0, 0, "\x00" x (0xFFFF + 1) ) } qr/^Invalid Argument: 'content' cannot exceed/; + +# build_record(type, request_id [, content]) +for (0..1, 4) { + throws_ok { build_record((1) x $_) } qr/^Usage: /; +} + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/015_build_stream.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/015_build_stream.t new file mode 100644 index 000000000..233f225fd --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/015_build_stream.t @@ -0,0 +1,82 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 12; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_stream ]); +} + +sub TRUE () { !!1 } +sub FALSE () { !!0 } + +my @tests = ( + # expected, type, request_id, content, terminate + [ "", 1, 1, '', FALSE ], + [ "", 1, 1, undef, FALSE ], + [ "\x01\x01\x00\x01\x00\x00\x00\x00", 1, 1, '', TRUE ], + [ "\x01\x01\x00\x01\x00\x00\x00\x00", 1, 1, undef, TRUE ], + + [ "\x01\x01\x00\x01\x00\x03\x05\x00" + . "FOO\x00\x00\x00\x00\x00", 1, 1, 'FOO', FALSE ], + + [ "\x01\x01\x00\x01\x00\x03\x05\x00" + . "FOO\x00\x00\x00\x00\x00" + . "\x01\x01\x00\x01\x00\x00\x00\x00", 1, 1, 'FOO', TRUE ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_stream(@$test[1..4]); + is_hexstr($got, $expected, 'build_stream()'); +} + +{ + my $header = "\x01\x01\x00\x01\x7F\xF8\x00\x00"; + my $content = "x" x 32760; + my $trailer = "\x01\x01\x00\x01\x00\x00\x00\x00"; + + { + my $expected = $header . $content; + my $got = build_stream(1,1, $content); + is_hexstr($got, $expected, 'build_stream(content_length: 32760 terminate:false)'); + } + + { + my $expected = $header . $content . $trailer; + my $got = build_stream(1,1, $content, 1); + is_hexstr($got, $expected, 'build_stream(content_length: 32760 terminate:true)'); + } +} + +{ + my $records = "\x01\x01\x00\x01\x7F\xF8\x00\x00" # H1 + . "x" x 32760 # C1 + . "\x01\x01\x00\x01\x00\x08\x00\x00" # H2 + . "x" x 8 # C2 + ; + my $content = "x" x 32768; + my $trailer = "\x01\x01\x00\x01\x00\x00\x00\x00"; + + { + my $expected = $records; + my $got = build_stream(1,1, $content); + is_hexstr($got, $records, 'build_stream(content_length: 32768 terminate:false)'); + } + + { + my $expected = $records . $trailer; + my $got = build_stream(1,1, $content, 1); + is_hexstr($got, $expected, 'build_stream(content_length: 32768 terminate:true)'); + } +} + +throws_ok { build_stream() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/020_begin_request_body.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/020_begin_request_body.t new file mode 100644 index 000000000..031a7d173 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/020_begin_request_body.t @@ -0,0 +1,41 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 9; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_begin_request_body + parse_begin_request_body ]); +} + +my @tests = ( + # octets role flags + [ "\x00\x00\x00\x00\x00\x00\x00\x00", 0, 0 ], + [ "\xFF\xFF\xFF\x00\x00\x00\x00\x00", 0xFFFF, 0xFF ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_begin_request_body(@$test[1..2]); + is_hexstr($got, $expected, 'build_begin_request_body()'); +} + +foreach my $test (@tests) { + my @expected = @$test[1..2]; + my @got = parse_begin_request_body($test->[0]); + is_deeply(\@got, \@expected, "parse_begin_request_body()"); +} + +throws_ok { parse_begin_request_body("") } qr/^FastCGI: Insufficient .* FCGI_BeginRequestBody/; +throws_ok { parse_begin_request_body(undef) } qr/^FastCGI: Insufficient .* FCGI_BeginRequestBody/; + +throws_ok { build_begin_request_body() } qr/^Usage: /; +throws_ok { parse_begin_request_body() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/025_begin_request_record.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/025_begin_request_record.t new file mode 100644 index 000000000..50f944321 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/025_begin_request_record.t @@ -0,0 +1,30 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 4; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_begin_request_record ]); +} + +my @tests = ( + # octets request_id role flags + [ "\x01\x01\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 0, 0 ], + [ "\x01\x01\xFF\xFF\x00\x08\x00\x00\xFF\xFF\xFF\x00\x00\x00\x00\x00", 0xFFFF, 0xFFFF, 0xFF ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_begin_request_record(@$test[1..3]); + is_hexstr($got, $expected, 'build_begin_request_record()'); +} + +throws_ok { build_begin_request_record() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/027_begin_request.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/027_begin_request.t new file mode 100644 index 000000000..e5d6c91f6 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/027_begin_request.t @@ -0,0 +1,97 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 15; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_begin_request ]); + use_ok('Net::FastCGI::Constant', qw[ :type :role ]); +} + +{ + my $begin = "\x01\x01\x00\x01\x00\x08\x00\x00" # FCGI_Header id=1 + . "\x00\x01\x00\x00\x00\x00\x00\x00"; # FCGI_BeginRequestBody role=FCGI_RESPONDER + + my $params = "\x01\x04\x00\x01\x00\x00\x00\x00"; # FCGI_Header type=FCGI_PARAMS + + { + my $exp = $begin . $params; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, {}); + is_hexstr($got, $exp, q); + } + + my $stdin = "\x01\x05\x00\x01\x00\x00\x00\x00"; # FCGI_Header type=FCGI_STDIN + + { + my $exp = $begin . $params . $stdin; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, {}, ''); + is_hexstr($got, $exp, q); + } + + { + my $exp = $begin . $params . $stdin; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, {}, undef); + is_hexstr($got, $exp, q); + } + + my $data = "\x01\x08\x00\x01\x00\x00\x00\x00"; # FCGI_Header type=FCGI_DATA + + { + my $exp = $begin . $params . $stdin . $data; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, {}, '', undef); + is_hexstr($got, $exp, q); + } + + { + my $exp = $begin . $params . $stdin . $data; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, {}, undef, ''); + is_hexstr($got, $exp, q); + } +} + +{ + my $begin = "\x01\x01\x00\x01\x00\x08\x00\x00" # FCGI_Header id=1 + . "\x00\x01\x00\x00\x00\x00\x00\x00"; # FCGI_BeginRequestBody role=FCGI_RESPONDER + + my $params = "\x01\x04\x00\x01\x00\x08\x00\x00" # FCGI_Header type=FCGI_PARAMS + . "\x03\x03FooBar" + . "\x01\x04\x00\x01\x00\x00\x00\x00"; + + { + my $exp = $begin . $params; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, { Foo => 'Bar' }); + is_hexstr($got, $exp, q!build_begin_request(1, FCGI_RESPONDER, 0, { Foo => 'Bar' })!); + } + + my $stdin = "\x01\x05\x00\x01\x03\xFC\x04\x00" # FCGI_Header type=FCGI_STDIN + . "x" x 1020 . "\0" x 4 + . "\x01\x05\x00\x01\x00\x00\x00\x00"; + { + my $exp = $begin . $params . $stdin; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, { Foo => 'Bar' }, 'x' x 1020); + is_hexstr($got, $exp, q!build_begin_request(1, FCGI_RESPONDER, 0, { Foo => 'Bar' }, 'x' x 1020)!); + } + + my $data = "\x01\x08\x00\x01\x04\x00\x00\x00" # FCGI_Header type=FCGI_DATA + . "y" x 1024 + . "\x01\x08\x00\x01\x00\x00\x00\x00"; + + { + my $exp = $begin . $params . $stdin . $data; + my $got = build_begin_request(1, FCGI_RESPONDER, 0, { Foo => 'Bar' }, 'x' x 1020, 'y' x 1024); + is_hexstr($got, $exp, q!build_begin_request(1, FCGI_RESPONDER, 0, { Foo => 'Bar' }, 'x' x 1020, 'y' x 1024)!); + } +} + +# build_begin_request(request_id, role, flags, params [, stdin [, data]]) +for (0..3, 7) { + throws_ok { build_begin_request((1) x $_) } qr/^Usage: /; +} + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/030_end_request_body.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/030_end_request_body.t new file mode 100644 index 000000000..783408d4a --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/030_end_request_body.t @@ -0,0 +1,42 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 9; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_end_request_body + parse_end_request_body ]); +} + +my @tests = ( + # octets app_status protocol_status + [ "\x00\x00\x00\x00\x00\x00\x00\x00", 0, 0 ], + [ "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", 0xFFFFFFFF, 0xFF ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_end_request_body(@$test[1..2]); + is_hexstr($got, $expected, 'build_end_request_body()'); +} + +foreach my $test (@tests) { + my @expected = @$test[1..2]; + my @got = parse_end_request_body($test->[0]); + is_deeply(\@got, \@expected, "parse_end_request_body()"); +} + + +throws_ok { parse_end_request_body("") } qr/^FastCGI: Insufficient .* FCGI_EndRequestBody/; +throws_ok { parse_end_request_body(undef) } qr/^FastCGI: Insufficient .* FCGI_EndRequestBody/; + +throws_ok { build_end_request_body() } qr/^Usage: /; +throws_ok { parse_end_request_body() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/035_end_request_record.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/035_end_request_record.t new file mode 100644 index 000000000..f76dbd1f2 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/035_end_request_record.t @@ -0,0 +1,30 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 4; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_end_request_record ]); +} + +my @tests = ( + # octets request_id app_status protocol_status + [ "\x01\x03\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 0, 0 ], + [ "\x01\x03\xFF\xFF\x00\x08\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", 0xFFFF, 0xFFFFFFFF, 0xFF ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_end_request_record(@$test[1..3]); + is_hexstr($got, $expected, 'build_end_request_record()'); +} + +throws_ok { build_end_request_record() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/037_end_request.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/037_end_request.t new file mode 100644 index 000000000..c7c421c54 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/037_end_request.t @@ -0,0 +1,87 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 13; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_end_request ]); + use_ok('Net::FastCGI::Constant', qw[ :type :protocol_status ]); +} + +{ + my $end = "\x01\x03\x00\x01\x00\x08\x00\x00" # FCGI_Header id=1 + . "\x00\x00\x00\x00\x00\x00\x00\x00" # FCGI_EndRequestBody + ; + + { + my $got = build_end_request(1, 0, FCGI_REQUEST_COMPLETE); + is_hexstr($got, $end, q); + } + + my $stdout = "\x01\x06\x00\x01\x00\x00\x00\x00"; # FCGI_Header type=FCGI_STDOUT + + { + my $exp = $stdout . $end; + my $got = build_end_request(1, 0, FCGI_REQUEST_COMPLETE, ''); + is_hexstr($got, $exp, q); + } + + { + my $exp = $stdout . $end; + my $got = build_end_request(1, 0, FCGI_REQUEST_COMPLETE, undef); + is_hexstr($got, $exp, q); + } + + my $stderr = "\x01\x07\x00\x01\x00\x00\x00\x00"; # FCGI_Header type=FCGI_STDERR + + { + my $exp = $stdout . $stderr . $end; + my $got = build_end_request(1, 0, FCGI_REQUEST_COMPLETE, '', undef); + is_hexstr($got, $exp, q); + } + + { + my $exp = $stdout . $stderr . $end; + my $got = build_end_request(1, 0, FCGI_REQUEST_COMPLETE, undef, ''); + is_hexstr($got, $exp, q); + } +} + +{ + my $end = "\x01\x03\x00\x01\x00\x08\x00\x00" # FCGI_Header id=1 + . "\x00\x00\x00\x00\x00\x00\x00\x00" # FCGI_EndRequestBody + ; + + my $stdout = "\x01\x06\x00\x01\x03\xFC\x04\x00" # FCGI_Header type=FCGI_STDOUT + . "x" x 1020 . "\0" x 4 + . "\x01\x06\x00\x01\x00\x00\x00\x00"; + + { + my $exp = $stdout . $end; + my $got = build_end_request(1, 0, FCGI_REQUEST_COMPLETE, 'x' x 1020); + is_hexstr($got, $exp, q); + } + + my $stderr = "\x01\x07\x00\x01\x04\x00\x00\x00" # FCGI_Header type=FCGI_STDERR + . "y" x 1024 + . "\x01\x07\x00\x01\x00\x00\x00\x00"; + + { + my $exp = $stdout . $stderr . $end; + my $got = build_end_request(1, 0, FCGI_REQUEST_COMPLETE, 'x' x 1020, 'y' x 1024); + is_hexstr($got, $exp, q); + } +} + +# build_end_request(request_id, app_status, protocol_status [, stdout [, stderr]]) +for (0..2, 6) { + throws_ok { build_end_request((1) x $_) } qr/^Usage: /; +} + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/040_unknown_type_body.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/040_unknown_type_body.t new file mode 100644 index 000000000..27e0d3791 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/040_unknown_type_body.t @@ -0,0 +1,42 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 9; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_unknown_type_body + parse_unknown_type_body ]); +} + +my @tests = ( + # octets type + [ "\x00\x00\x00\x00\x00\x00\x00\x00", 0 ], + [ "\xFF\x00\x00\x00\x00\x00\x00\x00", 0xFF ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_unknown_type_body($test->[1]); + is_hexstr($got, $expected, 'build_unknown_type_body()'); +} + +foreach my $test (@tests) { + my @expected = $test->[1]; + my @got = parse_unknown_type_body($test->[0]); + is_deeply(\@got, \@expected, "parse_unknown_type_body()"); +} + + +throws_ok { parse_unknown_type_body("") } qr/^^FastCGI: Insufficient .* FCGI_UnknownTypeBody/; +throws_ok { parse_unknown_type_body(undef) } qr/^^FastCGI: Insufficient .* FCGI_UnknownTypeBody/; + +throws_ok { build_unknown_type_body() } qr/^Usage: /; +throws_ok { parse_unknown_type_body() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/045_unknown_type_record.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/045_unknown_type_record.t new file mode 100644 index 000000000..8ee053ab2 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/045_unknown_type_record.t @@ -0,0 +1,30 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 4; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_unknown_type_record ]); +} + +my @tests = ( + # octets type + [ "\x01\x0B\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0 ], + [ "\x01\x0B\x00\x00\x00\x08\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00", 0xFF ], +); + +foreach my $test (@tests) { + my $expected = $test->[0]; + my $got = build_unknown_type_record($test->[1]); + is_hexstr($got, $expected, 'build_unknown_type_record()'); +} + +throws_ok { build_unknown_type_record() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/050_parse_record.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/050_parse_record.t new file mode 100644 index 000000000..34c5fb923 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/050_parse_record.t @@ -0,0 +1,180 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 54; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Constant', qw[:all]); + use_ok('Net::FastCGI::Protocol', qw[ build_header + build_record + build_stream + parse_record ]); +} + +my @records_ok = ( + [ + "\x01\x01\x00\x01\x00\x08\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00", + "\x00\x01\x00\x00\x00\x00\x00\x00", + { type => FCGI_BEGIN_REQUEST, + request_id => 1, + role => FCGI_RESPONDER, + flags => 0 } + ], + [ + "\x01\x02\x00\x01\x00\x00\x00\x00", + "", + { type => FCGI_ABORT_REQUEST, + request_id => 1 } + ], + [ + "\x01\x03\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "\x00\x00\x00\x00\x00\x00\x00\x00", + { type => FCGI_END_REQUEST, + request_id => 1, + protocol_status => 0, + app_status => 0 } + ], + [ + "\x01\x04\x00d\x00\x0B\x05\x00FCGI_PARAMS\x00\x00\x00\x00\x00", + "FCGI_PARAMS", + { type => FCGI_PARAMS, + request_id => 100, + content => 'FCGI_PARAMS' } + ], + [ + "\x01\x05\x00\xC8\x00\x0A\x06\x00FCGI_STDIN\x00\x00\x00\x00\x00\x00", + "FCGI_STDIN", + { type => FCGI_STDIN, + request_id => 200, + content => 'FCGI_STDIN' } + ], + [ + "\x01\x06\x01\x2C\x00\x0B\x05\x00FCGI_STDOUT\x00\x00\x00\x00\x00", + "FCGI_STDOUT", + { type => FCGI_STDOUT, + request_id => 300, + content => 'FCGI_STDOUT' } + ], + [ + "\x01\x07\x01\x90\x00\x0B\x05\x00FCGI_STDERR\x00\x00\x00\x00\x00", + "FCGI_STDERR", + { type => FCGI_STDERR, + request_id => 400, + content => 'FCGI_STDERR' } + ], + [ + "\x01\x08\x01\xF4\x00\x09\x07\x00FCGI_DATA\x00\x00\x00\x00\x00\x00\x00", + "FCGI_DATA", + { type => FCGI_DATA, + request_id => 500, + content => 'FCGI_DATA' } + ], + [ + "\x01\x09\x00\x00\x00\x0D\x03\x00\x03\x03BarBaZ\x03\x00FOO\x00\x00\x00", + "\x03\x03BarBaZ\x03\x00FOO", + { type => FCGI_GET_VALUES, + request_id => FCGI_NULL_REQUEST_ID, + values => { FOO => '', Bar => 'BaZ' } + } + ], + [ + "\x01\x0A\x00\x00\x00\x17\x01\x00\x04\x01BETA2\x05\x01ALPHA1\x05\x01GAMMA3\x00", + "\x04\x01BETA2\x05\x01ALPHA1\x05\x01GAMMA3", + { type => FCGI_GET_VALUES_RESULT, + request_id => FCGI_NULL_REQUEST_ID, + values => { ALPHA => 1, BETA => 2, GAMMA => 3 } + } + ], + [ + "\x01\x0B\x00\x00\x00\x08\x00\x00\x64\x00\x00\x00\x00\x00\x00\x00", + "\x64\x00\x00\x00\x00\x00\x00\x00", + { type => FCGI_UNKNOWN_TYPE, + request_id => FCGI_NULL_REQUEST_ID, + unknown_type => 100 } + ], + [ + "\x01\x6F\x00\xDE\x00\x04\x04\x00oops\x00\x00\x00\x00", + "oops", + { type => 111, + request_id => 222, + content => 'oops' } + ], + [ + "\x01\xFF\xFF\xFF\x00\x00\x00\x00", + "", + { type => 0xFF, + request_id => 0xFFFF } + ], +); + +foreach my $test (@records_ok) { + my $expected = $test->[2]; + my $got = parse_record($test->[0]); + is_deeply($got, $expected, "parse_record() in scalar context"); +} + +foreach my $test (@records_ok) { + my @expected = ($test->[2]->{type}, $test->[2]->{request_id}, $test->[1]); + my @got = parse_record($test->[0]); + is_deeply(\@got, \@expected, "parse_record() in list context"); +} + +my @headers_malformed = ( + # type, request_id, content_length, padding_length + [ FCGI_BEGIN_REQUEST, 0, 0, 0 ], + [ FCGI_BEGIN_REQUEST, 1, 0, 0 ], + [ FCGI_ABORT_REQUEST, 0, 0, 0 ], + [ FCGI_END_REQUEST, 0, 0, 0 ], + [ FCGI_END_REQUEST, 1, 0, 0 ], + [ FCGI_PARAMS, 0, 0, 0 ], + [ FCGI_STDIN, 0, 0, 0 ], + [ FCGI_STDOUT, 0, 0, 0 ], + [ FCGI_STDERR, 0, 0, 0 ], + [ FCGI_DATA, 0, 0, 0 ], + [ FCGI_GET_VALUES, 1, 0, 0 ], + [ FCGI_GET_VALUES_RESULT, 1, 0, 0 ], + [ FCGI_UNKNOWN_TYPE, 0, 0, 0 ], + [ FCGI_UNKNOWN_TYPE, 1, 0, 0 ] +); + +foreach my $test (@headers_malformed) { + my $octets = build_header(@$test); + throws_ok { parse_record($octets) } qr/^FastCGI: Malformed/; +} + +{ + my $octets = build_header(FCGI_ABORT_REQUEST, 1, 8, 0) . "\x00" x 8; + throws_ok { parse_record($octets) } qr/^FastCGI: Malformed/; +} + +my @stream_types = ( + FCGI_PARAMS, + FCGI_STDIN, + FCGI_STDOUT, + FCGI_STDERR, + FCGI_DATA +); + +foreach my $type (@stream_types) { + my $expected = { type => $type, request_id => 1, content => '' }; + my $octets = build_record($type, 1, ''); + my $got = parse_record($octets); + is_deeply($got, $expected, "parse_record(stream record) in scalar context"); +} + +foreach my $type (@stream_types) { + my @expected = ($type, 1, ''); + my $octets = build_record($type, 1, ''); + my @got = parse_record($octets); + is_deeply(\@got, \@expected, "parse_record(stream record) in list context"); +} + +throws_ok { parse_record() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/055_parse_record_body.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/055_parse_record_body.t new file mode 100644 index 000000000..35f3793c6 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/055_parse_record_body.t @@ -0,0 +1,98 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 33; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Constant', qw[:all]); + use_ok('Net::FastCGI::Protocol', qw[ build_header + build_record + build_stream + parse_record_body ]); +} + +my @ok = ( + [ + "\x00\x01\x01\x00\x00\x00\x00\x00", + { type => FCGI_BEGIN_REQUEST, + request_id => 1, + role => 1, + flags => 1 } + ], + [ + "\x00\x00\x00\x01\x01\x00\x00\x00", + { type => FCGI_END_REQUEST, + request_id => 1, + app_status => 1, + protocol_status => 1 } + ], + [ + undef, + { type => FCGI_STDIN, + request_id => 1, + content => '' } + ], + [ + "", + { type => FCGI_PARAMS, + request_id => 1, + content => '' } + ], + [ + "\x01\x01A1\x01\x01B2", + { type => FCGI_GET_VALUES, + request_id => FCGI_NULL_REQUEST_ID, + values => { A => 1, B => 2 } } + ], + [ + undef, + { type => FCGI_GET_VALUES_RESULT, + request_id => FCGI_NULL_REQUEST_ID, + values => {} } + ] +); + +foreach my $test (@ok) { + my $exp = $test->[1]; + my $got = parse_record_body($exp->{type}, $exp->{request_id}, $test->[0]); + is_deeply($got, $exp, "parse_record_body()"); +} + +my @malformed = ( + # type, request_id + [ FCGI_BEGIN_REQUEST, 0 ], + [ FCGI_END_REQUEST, 0 ], + [ FCGI_PARAMS, 0 ], + [ FCGI_STDIN, 0 ], + [ FCGI_STDOUT, 0 ], + [ FCGI_STDERR, 0 ], + [ FCGI_DATA, 0 ], + [ FCGI_GET_VALUES, 1 ], + [ FCGI_GET_VALUES_RESULT, 1 ], + [ FCGI_UNKNOWN_TYPE, 1 ] +); + +foreach my $test (@malformed) { + my ($type, $request_id) = @$test; + throws_ok { parse_record_body($type, $request_id, '') } qr/^FastCGI: Malformed/; +} + +{ + my $content = "\x00" x (FCGI_MAX_CONTENT_LEN + 1); + foreach my $type (0..12) { + throws_ok { parse_record_body($type, 0, $content) } qr/^Invalid Argument: 'content' cannot exceed/; + } +} + +# parse_record_body(type, request_id, content) +for (0, 4) { + throws_ok { parse_record_body((1) x $_) } qr/^Usage: /; +} + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/060_params.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/060_params.t new file mode 100644 index 000000000..92d9a64c7 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/060_params.t @@ -0,0 +1,79 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 38; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[ build_params + check_params + parse_params ]); +} + +sub TRUE () { !!1 } +sub FALSE () { !!0 } + +my @tests = ( + # octets params + [ "", { } ], + [ "\x00\x00", { '' => '' }, ], + [ "\x01\x01\x31\x31", { 1 => 1 }, ], + [ "\x01\x01\x41\x42\x01\x01\x43\x44\x01\x01\x45\x46", { A => 'B', C => 'D', E => 'F' } ], +); + +foreach my $test (@tests) { + my ($expected, $params) = @$test; + my $got = join '', map { + build_params({ $_ => $params->{$_} }) + } sort keys %$params; + is_hexstr($got, $expected, 'build_params()'); +} + +is_hexstr("\x03\x00foo", build_params({foo => undef}), 'build_params({foo => undef})'); +is_hexstr("\x7F\x00" . "x" x 127, build_params({ "x" x 127 => '' })); +is_hexstr("\x00\x7F" . "x" x 127, build_params({ '' => "x" x 127 })); +is_hexstr("\x80\x00\x00\x80\x00" . "x" x 128, build_params({ "x" x 128 => '' })); +is_hexstr("\x00\x80\x00\x00\x80" . "x" x 128, build_params({ '' => "x" x 128 })); + +foreach my $test (@tests) { + my $expected = $test->[1]; + my $got = parse_params($test->[0]); + is_deeply($got, $expected, 'parse_params()'); +} + +foreach my $test (@tests) { + my $octets = $test->[0]; + is(check_params($octets), TRUE, 'check_params(octets) eq TRUE'); +} + +my @insufficient = ( + "\x00", + "\x01", + "\x00\x01", + "\x01\x00", + "\x00\xFF", + "\x01\xFF\x00", + "\x00\x80\x00\x00\x80", + "\x80\x00\x00\x80\x00", +); + +foreach my $test (@insufficient) { + throws_ok { parse_params($test) } qr/^FastCGI: Insufficient .* FCGI_NameValuePair/; +} + +foreach my $test (@insufficient) { + is(check_params($test), FALSE, 'check_params(octets) eq FALSE'); +} + +is(check_params(undef), FALSE, 'check_params(undef) eq FALSE'); + +throws_ok { check_params() } qr/^Usage: /; +throws_ok { build_params() } qr/^Usage: /; +throws_ok { parse_params() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/065_record_type.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/065_record_type.t new file mode 100644 index 000000000..5836a05bc --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/065_record_type.t @@ -0,0 +1,105 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 55; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Constant', qw[ :type ] ); + use_ok('Net::FastCGI::Protocol', qw[ is_discrete_type + is_known_type + is_management_type + is_stream_type ] ); +} + +sub TRUE () { !!1 } +sub FALSE () { !!0 } + +{ + my @known = ( + FCGI_BEGIN_REQUEST, + FCGI_ABORT_REQUEST, + FCGI_END_REQUEST, + FCGI_PARAMS, + FCGI_STDIN, + FCGI_STDOUT, + FCGI_STDERR, + FCGI_DATA, + FCGI_GET_VALUES, + FCGI_GET_VALUES_RESULT, + FCGI_UNKNOWN_TYPE, + FCGI_MAXTYPE, + ); + + foreach my $type (@known) { + is( is_known_type($type), TRUE, qq/is_known_type($type) = true/ ); + } +} + +{ + my @discrete = ( + FCGI_BEGIN_REQUEST, + FCGI_ABORT_REQUEST, + FCGI_END_REQUEST, + FCGI_GET_VALUES, + FCGI_GET_VALUES_RESULT, + FCGI_UNKNOWN_TYPE, + ); + + foreach my $type ( @discrete ) { + is( is_stream_type($type), FALSE, qq/is_stream_type($type) = false/ ); + is( is_discrete_type($type), TRUE, qq/is_discrete_type($type) = true/ ); + } +} + +{ + my @management = ( + FCGI_GET_VALUES, + FCGI_GET_VALUES_RESULT, + FCGI_UNKNOWN_TYPE, + ); + + foreach my $type (@management) { + is( is_management_type($type), TRUE, qq/is_management_type($type) = true/ ); + } +} + +{ + my @stream = ( + FCGI_PARAMS, + FCGI_STDIN, + FCGI_STDOUT, + FCGI_STDERR, + FCGI_DATA, + ); + + foreach my $type (@stream) { + is( is_stream_type($type), TRUE, qq/is_stream_type($type) = true/ ); + is( is_discrete_type($type), FALSE, qq/is_discrete_type($type) = false/ ); + } +} + +{ + my @subnames = qw( + is_known_type + is_discrete_type + is_management_type + is_stream_type + ); + + foreach my $name (@subnames) { + my $sub = __PACKAGE__->can($name); + is($sub->($_), FALSE, qq/$name($_) = false/) for (-10, 0, 12); + } +} + +throws_ok { is_known_type() } qr/^Usage: /; +throws_ok { is_discrete_type() } qr/^Usage: /; +throws_ok { is_management_type() } qr/^Usage: /; +throws_ok { is_stream_type() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/070_names.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/070_names.t new file mode 100644 index 000000000..86af502d6 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/070_names.t @@ -0,0 +1,80 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 29; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Constant', qw[ :type :role :protocol_status ] ); + use_ok('Net::FastCGI::Protocol', qw[ get_type_name + get_role_name + get_protocol_status_name ] ); +} + +{ + my @tests = ( + [ FCGI_BEGIN_REQUEST, 'FCGI_BEGIN_REQUEST' ], + [ FCGI_ABORT_REQUEST, 'FCGI_ABORT_REQUEST' ], + [ FCGI_END_REQUEST, 'FCGI_END_REQUEST' ], + [ FCGI_PARAMS, 'FCGI_PARAMS' ], + [ FCGI_STDIN, 'FCGI_STDIN' ], + [ FCGI_STDOUT, 'FCGI_STDOUT' ], + [ FCGI_STDERR, 'FCGI_STDERR' ], + [ FCGI_DATA, 'FCGI_DATA' ], + [ FCGI_GET_VALUES, 'FCGI_GET_VALUES' ], + [ FCGI_GET_VALUES_RESULT, 'FCGI_GET_VALUES_RESULT' ], + [ FCGI_UNKNOWN_TYPE, 'FCGI_UNKNOWN_TYPE' ], + ); + + foreach my $test ( @tests ) { + my ( $type, $name ) = @$test; + is( get_type_name($type), $name, qq/get_type_name($type) = $name/ ); + } + + foreach my $type ( 0, 0xFF ) { + is(get_type_name($type), sprintf('0x%.2X', $type)); + } +} + +{ + my @tests = ( + [ FCGI_RESPONDER, 'FCGI_RESPONDER' ], + [ FCGI_AUTHORIZER, 'FCGI_AUTHORIZER' ], + [ FCGI_FILTER, 'FCGI_FILTER' ], + ); + + foreach my $test ( @tests ) { + my ( $role, $name ) = @$test; + is( get_role_name($role), $name, qq/get_role_name($role) = $name/ ); + } + + foreach my $role ( 0, 0xFF, 0xFFFF ) { + is(get_role_name($role), sprintf('0x%.4X', $role)); + } +} + +{ + my @tests = ( + [ FCGI_REQUEST_COMPLETE, 'FCGI_REQUEST_COMPLETE' ], + [ FCGI_CANT_MPX_CONN, 'FCGI_CANT_MPX_CONN' ], + [ FCGI_OVERLOADED, 'FCGI_OVERLOADED' ], + [ FCGI_UNKNOWN_ROLE, 'FCGI_UNKNOWN_ROLE' ], + ); + + foreach my $test ( @tests ) { + my ( $status, $name ) = @$test; + is( get_protocol_status_name($status), $name, qq/get_protocol_status_name($status) = $name/ ); + } + + is(get_protocol_status_name(0xFF), '0xFF'); +} + +throws_ok { get_type_name() } qr/^Usage: /; +throws_ok { get_role_name() } qr/^Usage: /; +throws_ok { get_protocol_status_name() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/080_dump_record.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/080_dump_record.t new file mode 100644 index 000000000..0e0bb5dee --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/080_dump_record.t @@ -0,0 +1,51 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 9; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Protocol', qw[build_record dump_record]); +} + +{ + my $record = build_record(0, 0, "\x00\x01\x02\x03\x04\x05\x06\x07"); + my $dump = dump_record($record); + like $dump, qr/\A \{0x00, \s 0, \s "\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07"\}/x; +} + +{ + my $record = build_record(0, 0, "\x00\x01\x02\x03\x04\x05\x06\x07"); + + for my $len (0, 8) { + my $dump = dump_record(substr($record, 0, $len)); + like $dump, qr/\A \{ Malformed \s FCGI_Record }/x, "Insufficient octets"; + } +} + +{ + for my $header ("\x00\x00\x00\x00\x00\x00\x00\x00", + "\xFF\x00\x00\x00\x00\x00\x00\x00") { + my $dump = dump_record($header); + like $dump, qr/\A \{ Malformed \s FCGI_Record }/x, "Protocol version mismatch"; + } +} + +# dump_record(type, request_id [, content]) deprecated +{ + my $dump = dump_record(0, 0); + like $dump, qr/\A \{0x00, \s 0, \s ""\}/x; +} +{ + my $dump = dump_record(0, 0, "\x00\x01\x02\x03\x04\x05\x06\x07"); + like $dump, qr/\A \{0x00, \s 0, \s "\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07"\}/x; +} + +# dump_record(octets) +throws_ok { dump_record() } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/085_dump_record_body.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/085_dump_record_body.t new file mode 100644 index 000000000..af00d7a33 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/020_protocol/085_dump_record_body.t @@ -0,0 +1,150 @@ +#!perl + +use strict; +use warnings; + +use lib 't/lib', 'lib'; +use myconfig; + +use Test::More tests => 64; +use Test::HexString; +use Test::Exception; + +BEGIN { + use_ok('Net::FastCGI::Constant', qw[:all]); + use_ok('Net::FastCGI::Protocol', qw[:all]); +} + +my @KNOWN_TYPES = ( + FCGI_BEGIN_REQUEST, + FCGI_ABORT_REQUEST, + FCGI_END_REQUEST, + FCGI_PARAMS, + FCGI_STDIN, + FCGI_STDOUT, + FCGI_STDERR, + FCGI_DATA, + FCGI_GET_VALUES, + FCGI_GET_VALUES_RESULT, + FCGI_UNKNOWN_TYPE, +); + +foreach my $type (@KNOWN_TYPES) { + like dump_record_body($type, 0), qr/\A\{ $FCGI_TYPE_NAME[$type]\, \s+ 0/x; +} + +foreach my $type (FCGI_PARAMS, FCGI_GET_VALUES, FCGI_GET_VALUES_RESULT) { + my $name = $FCGI_TYPE_NAME[$type]; + { + my $dump = dump_record_body($type, 1, ''); + like $dump, qr/\A \{ $name\, \s+ 1\, \s ""/x; + } + { + my $dump = dump_record_body($type, 1, build_params({ '' => '' })); + like $dump, qr/\A \{ $name\, \s+ 1\, \s "\\000\\000"/x; + } + { + my $dump = dump_record_body($type, 1, build_params({ 'Foo' => '' })); + like $dump, qr/\A \{ $name\, \s+ 1\, \s "\\003\\000Foo"/x; + } + { + my $dump = dump_record_body($type, 1, build_params({ "Foo\r\n" => "\x01\x02" })); + like $dump, qr/\A \{ $name\, \s+ 1\, \s "\\005\\002Foo\\r\\n\\x01\\x02/x; + } + { + my $dump = dump_record_body($type, 1, build_params({ 'x' => 'y' x 128 })); + like $dump, qr/\A \{ $name\, \s+ 1\, \s "\\001\\200\\000\\000\\200 x y+/x; + } + { + my $dump = dump_record_body($type, 1, "\001\001"); + like $dump, qr/\A \{ $name\, \s+ 1\, \s Malformed \s FCGI_NameValuePair/x; + } +} + +# Streams +{ + my @tests = ( + [ FCGI_STDIN, 1, "Foo\r\n\t", + qr/\A \{ FCGI_STDIN\, \s+ 1\, \s \"Foo\\r\\n\\t/x ], + [ FCGI_STDOUT, 1, "\x00\x01\x02\x03\x04\x05\x06\x07", + qr/\A \{ FCGI_STDOUT\, \s+ 1\, \s \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07/x ], + [ FCGI_STDERR, 1, "Foo \x01\x02 Bar\n", + qr/\A \{ FCGI_STDERR\, \s+ 1\, \s \"Foo\x20\\x01\\x02\x20Bar\\n/x ], + [ FCGI_DATA, 1, 'x' x 80, + qr/\A \{ FCGI_DATA\, \s+ 1\, \s \" x+ \s \.\.\./x ], + ); + + foreach my $test (@tests) { + my ($type, $request_id, $content, $expected) = @$test; + my $dump = dump_record_body($type, $request_id, $content); + like $dump, $expected; + } +} + +# FCGI_BEGIN_REQUEST +{ + my @tests = ( + [ build_begin_request_body(FCGI_RESPONDER, FCGI_KEEP_CONN), + qr/\A \{ FCGI_BEGIN_REQUEST\, \s+ 1\, \s \{ FCGI_RESPONDER\, \s+ FCGI_KEEP_CONN\}/x ], + [ build_begin_request_body(FCGI_FILTER, FCGI_KEEP_CONN | 0x10), + qr/\A \{ FCGI_BEGIN_REQUEST\, \s+ 1\, \s \{ FCGI_FILTER\, \s+ FCGI_KEEP_CONN|0x10\}/x ], + [ build_begin_request_body(FCGI_AUTHORIZER, 0), + qr/\A \{ FCGI_BEGIN_REQUEST\, \s+ 1\, \s \{ FCGI_AUTHORIZER\, \s+ 0\}/x ], + [ build_begin_request_body(0, 0x80), + qr/\A \{ FCGI_BEGIN_REQUEST\, \s+ 1\, \s \{ 0x0000\, \s+ 0x80\}/x ], + map([ $_, + qr/\A \{ FCGI_BEGIN_REQUEST\, \s+ 1\, \s \{ Malformed \s FCGI_BeginRequestBody/x ], + ('', "\x00" x 10)), + ); + + foreach my $test (@tests) { + my ($content, $expected) = @$test; + my $dump = dump_record_body(FCGI_BEGIN_REQUEST, 1, $content); + like $dump, $expected; + } +} + +# FCGI_END_REQUEST +{ + my @tests = ( + [ build_end_request_body(10, 0x80), + qr/\A \{ FCGI_END_REQUEST\, \s+ 1\, \s \{ 10\, \s+ 0x80\}/x ], + map([ $_, + qr/\A \{ FCGI_END_REQUEST\, \s+ 1\, \s \{ Malformed \s FCGI_EndRequestBody/x ], + ('', "\x00" x 10)), + map([ build_end_request_body(0, $_), + qr/\A \{ FCGI_END_REQUEST\, \s+ 1\, \s \{ 0\, \s+ $FCGI_PROTOCOL_STATUS_NAME[$_]\}/x ], + (0..3)), + ); + + foreach my $test (@tests) { + my ($content, $expected) = @$test; + my $dump = dump_record_body(FCGI_END_REQUEST, 1, $content); + like $dump, $expected; + } +} + +# FCGI_UNKNOWN_TYPE +{ + my @tests = ( + [ build_unknown_type_body(0), + qr/\A \{ FCGI_UNKNOWN_TYPE\, \s+ 0\, \s \{ 0/x ], + map([ build_unknown_type_body($_), + qr/\A \{ FCGI_UNKNOWN_TYPE\, \s+ 0\, \s \{ $FCGI_TYPE_NAME[$_]/x ], + @KNOWN_TYPES), + map([ $_, + qr/\A \{ FCGI_UNKNOWN_TYPE\, \s+ 0\, \s \{ Malformed \s FCGI_UnknownTypeBody/x ], + ('', "\x00" x 10)), + ); + + foreach my $test (@tests) { + my ($content, $expected) = @$test; + my $dump = dump_record_body(FCGI_UNKNOWN_TYPE, 0, $content); + like $dump, $expected; + } +} + + +throws_ok { dump_record_body() } qr/^Usage: /; +throws_ok { dump_record_body(0, 0, undef, 0) } qr/^Usage: /; + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/lib/myconfig.pm b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/lib/myconfig.pm new file mode 100644 index 000000000..1d4f63482 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/t/lib/myconfig.pm @@ -0,0 +1,9 @@ +package myconfig; + +use strict; + +BEGIN { + $ENV{NET_FASTCGI_PP} = 0 + !(-e "XS.xs" || -e "../XS.xs"); +} + +1; diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/000_pod.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/000_pod.t new file mode 100644 index 000000000..95dd65ae5 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/000_pod.t @@ -0,0 +1,17 @@ +#!perl + +use strict; +use warnings; + +use Test::More; + +BEGIN { + eval 'use Test::Pod'; + + if ($@) { + plan skip_all => 'Needs Test::Pod'; + } +} + +all_pod_files_ok(); + diff --git a/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/010_pod_coverage.t b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/010_pod_coverage.t new file mode 100644 index 000000000..bd4e3d225 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/p5-net-fastcgi/xt/010_pod_coverage.t @@ -0,0 +1,29 @@ +#!perl + +use strict; +use warnings; + +use Test::More; + +BEGIN { + eval 'use Test::Pod::Coverage'; + + if ($@) { + plan skip_all => 'Needs Test::Pod::Coverage'; + } +} + +my @modules = sort grep { !/::(?:PP|XS)$/ } all_modules(); + +plan tests => scalar(@modules); + +foreach my $module ( @modules ) { + my $params = {}; + + if ( $module =~ /^Net::FastCGI::Protocol$/ ) { + $params->{coverage_class} = 'Pod::Coverage::ExportOnly'; + } + + pod_coverage_ok( $module, $params ); +} + diff --git a/src/web/server/h2o/libh2o/misc/picotemplate-conf.pl b/src/web/server/h2o/libh2o/misc/picotemplate-conf.pl new file mode 100644 index 000000000..95eaa7a87 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/picotemplate-conf.pl @@ -0,0 +1,21 @@ +use strict; +use warnings; + +$main::push_expr = sub { + my $src = shift; + return qq{{ h2o_iovec_t _s = ($src); if (_s.len != 0 && _s.base[_s.len - 1] == '\\n') --_s.len; h2o_buffer_reserve(&_, _s.len); memcpy(_->bytes + _->size, _s.base, _s.len); _->size += _s.len; }}; +}; + +$main::push_void_expr = sub { + my $src = shift; + return qq{{ size_t _l = _->size; { $src }; if (_->size != 0 && _l != _->size && _->bytes[_->size - 1] == '\\n') --_->size; }}; +}; + +$main::push_str = sub { + my $str = shift; + $str =~ s/([\\'"])/\\$1/gs; + $str =~ s/\n/\\n/gs; + return $main::push_expr->(qq{h2o_iovec_init(H2O_STRLIT("$str"))}); +}; + +1; diff --git a/src/web/server/h2o/libh2o/misc/picotemplate/README.mkdn b/src/web/server/h2o/libh2o/misc/picotemplate/README.mkdn new file mode 100644 index 000000000..266f4bfa5 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/picotemplate/README.mkdn @@ -0,0 +1,42 @@ +PICOTEMPLATE - a tiny template engine for embedded HTML +======================================================= + +Picotemplate is a tiny template engine (preprocessor) designed to embed HTML (or other types of document) within the source code of any programming language. + +There are often cases where you would like to generate HTML within a program. In such cases, the easiest way would be to use a DSL (domain-specific language) to embed the HTML in the source code and preprocess it. Picotemplate is a preprocesser designed for such an use-case. + +As of now, Perl, C++, JavaScript, and JSX are the supported languages. + +THE LOGIC +--------- + +* lines starting with "?" are considered as embedded document +* within embedded document are considered as expressions (that return strings) +* the output is accumulated to variable named "_" (or $output in case of perl) +* filenames starting with an underscore (_) will be preprocessed, and the name of the output file will be the same omitting the leading underscore (e.g. _foo.cc will be preproccessed and converted to foo.cc) + +EXAMPLE +------- + +The following code (in _foo.cc) will be preprocessed and converted to a function (in foo.cc) that returns an function building an HTML snippet. + +``` +std::string unordered_list(const std::vector& strs) +{ + std::string _; // output is accumulated to _ +?
    + for (std::vector::const_iterator i = strs.begin(); + i != strs.end(); + ++i) { +?
  • + } +?
+ return _; +} +``` + +To preprocess a source file, simply run the command with the name of the source file to preprocess. The following exmaple preprocesses _foo.cc_ (template files should start with an underscore) and produces _foo.cc_. + +``` +$ picotemplate.pl _foo.cc +``` diff --git a/src/web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl b/src/web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl new file mode 100755 index 000000000..c49bf858f --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/picotemplate/picotemplate.pl @@ -0,0 +1,172 @@ +#! /usr/bin/perl + +# Copyright (c) 2012-2014 Kazuho Oku +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +use strict; +use warnings; +use Getopt::Long; + +my ($opt_conf, $opt_help); +GetOptions( + "conf=s" => \$opt_conf, + help => \$opt_help, +) or exit 1; + +if ($opt_help) { + print << "EOT"; +Usage: + $0 [--conf=] + +Description: + Applies necessary conversions against (the name should start + with an underscore) and emits the result. The result is stored in a file + which is given the same name as omitting the preceeding + underscore. + +Options: + --conf= if set, reads configuration from , + otherwise the configuration is taken from the + extension of + +EOT + exit 0; +} + +my $srcfn = shift @ARGV; +die "no filename" + unless $srcfn; + +our ($push_expr, $push_void_expr, $push_str); + +if ($opt_conf) { + require $opt_conf; +} else { + ($push_expr, $push_void_expr, $push_str) = convfunc($srcfn); +} + +$srcfn =~ m{^(.*/)_([^/]+)$} + or die "filename should start with an underscore"; +my $dstfn = "$1$2"; + +open my $srcfh, "<", $srcfn + or die "failed to open file:$srcfn"; +open my $dstfh, ">", $dstfn + or die "failed to open file:$dstfn"; + +my $pending = ''; +my $flush_pending = sub { + return if $pending eq ''; + print $dstfh $push_str->($pending), map { "\n" } @{[ $pending =~ /\n/g ]}; + $pending = ''; +}; +while (my $line = <$srcfh>) { + if ($line =~ /^\?/) { + my $markup = $'; + for my $token (split /(<\?[=!].*?\?>)/, $markup) { + if ($token =~ /^\<\?([=!])\s*(.*?)\s*\?>$/) { + $flush_pending->(); + if ($1 eq '=') { + print $dstfh $push_expr->($2); + } else { + print $dstfh $push_void_expr->($2); + } + } elsif ($token ne "") { + $pending .= $token; + } + } + } else { + $flush_pending->(); + print $dstfh "$line"; + } +} +$flush_pending->(); + +close $srcfh; +close $dstfh; + +sub convfunc { + my $srcfn = shift; + if ($srcfn =~ /\.pl$/) { + # perl + return ( + # push_expr function + sub { + my $src = shift; + return qq{\$_ = ($src); s/\\n\$//; \$output .= \$_;}; + }, + # push_void_expr function + sub { + my $src = shift; + return qq{do { $src };}; + }, + # push_str function + sub { + my $str = shift; + $str = quotemeta $str; + return qq{\$output .= "$str";}; + }, + ); + } elsif ($srcfn =~ /\.(?:cc|cpp|cxx)$/) { + # C++ + return ( + # push_expr function + sub { + my $src = shift; + return qq^{std::string _r; _r += ($src); if (! _r.empty() && *(_r.end() - 1) == '\\n') _r.erase(_r.size() - 1); _ += _r;}^; + }, + # push_void_expr function + sub { + my $src = shift; + return qq{{std::string::size_type __picotemplate_l = _.size(); { $src; } if (__picotemplate_l != _.size() && *(_.end() - 1) == '\\n') _.erase(_.size() - 1);}}; + }, + # push_str function + sub { + my $str = shift; + $str =~ s/([\\'"])/\\$1/gs; + $str =~ s/\n/\\n/gs; + return qq{_ += ("$str");}; + }, + ); + } elsif ($srcfn =~ /\.jsx?$/) { + # JavaScript or JSX + return ( + # push_expr function + sub { + my $src = shift; + return qq{_ += ($src).replace(/\\n\$/, "");}; + }, + # push_void_expr function + sub { + my $src = shift; + return qq{{ $src; }}; + }, + # push_str function + sub { + my $str = shift; + $str =~ s/([\\'"])/\\$1/gs; + $str =~ s/\n/\\n/gs; + return qq{_ += "$str";}; + }, + ); + } else { + die "unknown filetype!"; + } +} diff --git a/src/web/server/h2o/libh2o/misc/regen.mk b/src/web/server/h2o/libh2o/misc/regen.mk new file mode 100755 index 000000000..46417e7cf --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/regen.mk @@ -0,0 +1,45 @@ +define FATPACK_SHEBANG +#! /bin/sh +exec $${H2O_PERL:-perl} -x $$0 "$$@" +#! perl +endef +export FATPACK_SHEBANG + +all: tokens lib/handler/mruby/embedded.c.h lib/http2/hpack_huffman_table.h lib/handler/file/templates.c.h clang-format-all share/h2o/start_server share/h2o/fastcgi-cgi share/h2o/ca-bundle.crt + +tokens: + misc/tokens.pl + +lib/handler/mruby/embedded.c.h: misc/embed_mruby_code.pl \ + lib/handler/mruby/embedded/core.rb \ + lib/handler/mruby/embedded/http_request.rb \ + lib/handler/mruby/embedded/chunked.rb + misc/embed_mruby_code.pl $^ > $@ + clang-format -i $@ + +lib/http2/hpack_huffman_table.h: misc/mkhufftbl.py + python misc/mkhufftbl.py > $@ + +lib/handler/file/templates.c.h: misc/picotemplate-conf.pl lib/handler/file/_templates.c.h + misc/picotemplate/picotemplate.pl --conf misc/picotemplate-conf.pl lib/handler/file/_templates.c.h || exit 1 + clang-format -i $@ + +clang-format-all: + misc/clang-format-all.sh + +share/h2o/start_server: FORCE + cd misc/p5-Server-Starter; \ + fatpack-simple --shebang "$$FATPACK_SHEBANG" -o ../../$@ script/start_server + +share/h2o/fastcgi-cgi: FORCE + cd misc/p5-net-fastcgi; \ + fatpack-simple --shebang "$$FATPACK_SHEBANG" -o ../../$@ ../fastcgi-cgi.pl + +share/h2o/ca-bundle.crt: FORCE + cd share/h2o; \ + ../../misc/mk-ca-bundle.pl; \ + rm -f certdata.txt + +FORCE: + +.PHONY: tokens clang-format-all FORCE diff --git a/src/web/server/h2o/libh2o/misc/test-ca/README.md b/src/web/server/h2o/libh2o/misc/test-ca/README.md new file mode 100644 index 000000000..e39680b9c --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/test-ca/README.md @@ -0,0 +1,7 @@ +Run the commands below to create a new certificate signed by the CA: + +``` +openssl genrsa -out server.key 2048 +openssl req -days 3650 -new -key server.key -out server.csr +openssl ca -keyfile ca.key -cert ca.crt -extensions usr_cert -policy policy_anything -days 3650 -md sha256 -in server.csr -out server.crt +``` diff --git a/src/web/server/h2o/libh2o/misc/test-ca/ca.crt b/src/web/server/h2o/libh2o/misc/test-ca/ca.crt new file mode 100644 index 000000000..c08a1bcc5 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/test-ca/ca.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqDCCAZACCQCapLVXsO/NtjANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtI +Mk8gVGVzdCBDQTAeFw0xNDEyMTAwNjQ0NTVaFw0yNDEyMDcwNjQ0NTVaMBYxFDAS +BgNVBAMTC0gyTyBUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArzfuXVcDcEDcTfIMHb74mV8xaXrP7SqOJsd82/lyupBmnSlXgp8LgHj9vcHc +N12m2sQYkvdo50KJWRfljmzPQKlQ37r8D1focjVEDOc0wwan3UjB/Fg42ummUxeM +13kMukGy/osrQtgY8oFFV00HtAeNOLUF0FrpLy+beSuldoij8PFF3ADdEPay+TSy +tLIvrUrx4/tUv5yMEDL+HA1W0M8X/0L37+cXdT1bbINOBIJ/v6ZrpUSULCpmlR0n +XxDTCV1WWvlxMl1RcQrKvcV2NzeFnEFZI3aAdAkwpo3oUaidKG1g2GjFJA0PIhFz +hpQZkG+eyvRMYF5lPLVrCtZhkwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBItTZh +jyOOPyD4udv5K2qK5xRDGlSzdvqFgZ4ttffppk0XjgSd1ptNjqLnmhtvyzGz1hT3 +Byww/XiFZ+HDbCT7OGDzIByvUMPKpUD5KiTRaRsLbOd3aPIU1JbOYM7GTA921TZJ +qoCwWopAJB63LyoS+wAU4TZj0pUH4BqGsNeODA/XuPYPlUuB1bnfInRXBZiig/FT +JQ2Sab4O6bVRCWiHt9sM+waOCoX40VP7C8o+nYIR0w+calcRJ9GX8L4sR0jgZSQu +fjnp4ASeNZeSyivYvLC56pbBQBmgkMxl6lAvmmPnBKVHBlG+tBRyLZMnsO6ReP2R +UdScMm6oEnThC0k1 +-----END CERTIFICATE----- diff --git a/src/web/server/h2o/libh2o/misc/test-ca/ca.key b/src/web/server/h2o/libh2o/misc/test-ca/ca.key new file mode 100644 index 000000000..0e482de68 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/test-ca/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEArzfuXVcDcEDcTfIMHb74mV8xaXrP7SqOJsd82/lyupBmnSlX +gp8LgHj9vcHcN12m2sQYkvdo50KJWRfljmzPQKlQ37r8D1focjVEDOc0wwan3UjB +/Fg42ummUxeM13kMukGy/osrQtgY8oFFV00HtAeNOLUF0FrpLy+beSuldoij8PFF +3ADdEPay+TSytLIvrUrx4/tUv5yMEDL+HA1W0M8X/0L37+cXdT1bbINOBIJ/v6Zr +pUSULCpmlR0nXxDTCV1WWvlxMl1RcQrKvcV2NzeFnEFZI3aAdAkwpo3oUaidKG1g +2GjFJA0PIhFzhpQZkG+eyvRMYF5lPLVrCtZhkwIDAQABAoIBAFAS4W3qm/mqyoMc +gIj/gyCkLjKkG22pKvlx5Q+qOdIkt6BPClz5TgpSQLN9aqQqulvzjb9sLZayA46h +qxihhT1pozn7e8GPTP0Udprm9mfCHOi1nWEy+Bii+o2yj3zRJw3fhGtrMkQD+bKp +fQTegQy2/sTRhWyofzQ/L2/6kE1Qdid+iMR3J/qCgYKp7KGqn7bVGmuFx5l6hMsK +zSgmkubVFLAu6l23Wxni47IJb1iVVjDdfn4lbHo8PEolL4676FxFhKiyvE5/TRxD ++x6Fqp/IhFW1WCzszfyMnpW8pHOxhMHBkmVQBNQM4BtADZWAIlKPpqo2hCbtCj1c +F8ObSlECgYEA6BjUO6843IkkJ+KZNodEArbt9BUpuHvU/GmFDs31lkJfenfgOo5X +h6pvi8IT2Dpdc/jNyeQUSW1Rq5U4/yjoXBMT+/4bpnCyaYUcB8sGdxA+HmC4NFnv +9J8mWMWLnNnLMxcoEdfxsCj4MymjmPb3eDdQN3ijPCNYz+TZRN8B9LcCgYEAwUOE +NPA/efF3ZEoV8ZcrmGSaWjOts3O/XsegEaQeMCQ9s5rgqHODWUg/kOJpkUurPINo +lB2j1TO8uV9NxgSErwhbboQfPaR9UQa+4hvAKpzjCrHNght9DPTzkdqn+r30jqyl +VK/bomzwEbMOO8Ay3TE9aYcqL5h84WnKOUlCNgUCgYEAz6oXiud2iuwx63X1/DH/ +Cia+tZuR3rz9jnKjWOGzESSF5UAI4puleX9nmralptxh7Xf6ON/XvvMPFeTIQJVL +gcaYJOnxt3QyTfOJZvei6zkfO3LynfSynD8hwl/OUSMVirr3YS/D0gh2OuDQvfaK +6YckQN2xwAcoWaq7M8INL/UCgYEAuIIk7gWQFHObcHvqh0Ozs0IYupUr+G9A/NPI +1g7BY2S2I4pxgWEGvW5kgYYnEzgSuysZ5I6HozxJqRB7x9j4c8Kex9wLl2Y63eh6 +xJOJ2yT8/XDSqqxjrsMM5urCBHjlynC5Ryi9n0wxxjQUGqdb6CDiaKVKeTYzw2Ck +WBzAwaECgYEA0hZtjC3vbL6YZ4NKw53aVMyb7GNl/J3re2g8ZGlOgOBWWgW5BI72 +l917eDBx/GOmZ1lRsI91ai19CyxiyRqd0WcZ4pqQ2qRniq7E0kajoy4+kg8Aj27f +lD29WnSAse3sFHEhvoJYspZMGmKL4SLydXtVvllXeFhqYdK3773bPaE= +-----END RSA PRIVATE KEY----- diff --git a/src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt b/src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt new file mode 100644 index 000000000..0f465bc0b --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt @@ -0,0 +1,2 @@ +V 241207193305Z 01 unknown /CN=127.0.0.1.xip.io +V 241207201102Z 02 unknown /CN=alternate.127.0.0.1.xip.io diff --git a/src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt.attr b/src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt.attr new file mode 100644 index 000000000..8f7e63a34 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/test-ca/demoCA/index.txt.attr @@ -0,0 +1 @@ +unique_subject = yes diff --git a/src/web/server/h2o/libh2o/misc/test-ca/demoCA/newcerts/.gitkeep b/src/web/server/h2o/libh2o/misc/test-ca/demoCA/newcerts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/web/server/h2o/libh2o/misc/test-ca/demoCA/serial b/src/web/server/h2o/libh2o/misc/test-ca/demoCA/serial new file mode 100644 index 000000000..75016ea36 --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/test-ca/demoCA/serial @@ -0,0 +1 @@ +03 diff --git a/src/web/server/h2o/libh2o/misc/tokens.pl b/src/web/server/h2o/libh2o/misc/tokens.pl new file mode 100755 index 000000000..1fcf173fa --- /dev/null +++ b/src/web/server/h2o/libh2o/misc/tokens.pl @@ -0,0 +1,234 @@ +#! /usr/bin/perl + +# Copyright (c) 2014 DeNA Co., Ltd. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +use strict; +use warnings; +use List::Util qw(max); +use List::MoreUtils qw(uniq); +use Text::MicroTemplate qw(render_mt); + +use constant LICENSE => << 'EOT'; +/* + * Copyright (c) 2014 DeNA Co., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +EOT + +my %tokens; +my @hpack; + +while (my $line = ) { + chomp $line; + my ($hpack_index, $proxy_should_drop_for_req, $proxy_should_drop_for_res, $is_init_header_special, $http2_should_reject, $copy_for_push_request, $dont_compress, $name, $value) = + split /\s+/, $line, 9; + next unless $name ne ''; + $tokens{$name} = [ $hpack_index, $proxy_should_drop_for_req, $proxy_should_drop_for_res, $is_init_header_special, $http2_should_reject, $copy_for_push_request, $dont_compress ] + unless defined $tokens{$name}; + if ($hpack_index != 0) { + $hpack[$hpack_index - 1] = [ $name, $value ]; + } +} + +my @tokens = map { [ $_, @{$tokens{$_}} ] } uniq sort keys %tokens; + +# generate token.h +open my $fh, '>', 'include/h2o/token.h' + or die "failed to open include/h2o/token.h:$!"; +print $fh render_mt(<< 'EOT', \@tokens, LICENSE)->as_string; +? my ($tokens, $license) = @_; + +/* DO NOT EDIT! generated by tokens.pl */ +#ifndef h2o__token_h +#define h2o__token_h + +? for my $i (0..$#$tokens) { +#define [$i][0]) ?> (h2o__tokens + ) +? } + +#endif +EOT +close $fh; + +# generate token_table.h +open $fh, '>', 'lib/core/token_table.h' + or die "failed to open lib/core/token_table.h:$!"; +print $fh render_mt(<< 'EOT', \@tokens, LICENSE)->as_string; +? my ($tokens, $license) = @_; + +/* DO NOT EDIT! generated by tokens.pl */ +h2o_token_t h2o__tokens[] = { +? for my $i (0..$#$tokens) { + { { H2O_STRLIT("[$i][0] ?>") }, [$i][$_] } (1..$#{$tokens->[$i]})) ?> } +? } +}; +size_t h2o__num_tokens = ; + +const h2o_token_t *h2o_lookup_token(const char *name, size_t len) +{ + switch (len) { +? for my $len (uniq sort { $a <=> $b } map { length $_->[0] } @$tokens) { + case : + switch (name[]) { +? my @tokens_of_len = grep { length($_->[0]) == $len } @$tokens; +? for my $end (uniq sort map { substr($_->[0], length($_->[0]) - 1) } @tokens_of_len) { + case '': +? my @tokens_of_end = grep { substr($_->[0], length($_->[0]) - 1) eq $end } @tokens_of_len; +? for my $token (@tokens_of_end) { + if (memcmp(name, "[0], 0, length($token->[0]) - 1) ?>", [0]) - 1 ?>) == 0) + return [0]) ?>; +? } + break; +? } + } + break; +? } + } + + return NULL; +} +EOT +close $fh; + +# generate hpack_static_table.h +open $fh, '>', 'lib/http2/hpack_static_table.h' + or die "failed to open lib/hpack_static_table.h:$!"; +print $fh render_mt(<< 'EOT', \@hpack, LICENSE)->as_string; +? my ($entries, $license) = @_; + +/* automatically generated by tokens.pl */ + +static const struct st_h2o_hpack_static_table_entry_t h2o_hpack_static_table[] = { +? for my $i (0..$#$entries) { + { [$i][0]) ?>, { H2O_STRLIT("[$i][1] || "" ?>") } } +? } +}; +EOT +close $fh; + +sub normalize_name { + my $n = shift; + $n =~ s/^://; + $n =~ s/-/_/g; + $n =~ tr/a-z/A-Z/; + "H2O_TOKEN_$n"; +} + +# The table below is used to generate h2o__tokens in lib/core/token_table.h +# +# Meaning of the fields: +# - HTTP/2 static table index (non-zero if present) +# - Proxy should drop in request +# - Proxy should drop in response +# - Is init header special +# - HTTP/2 should reject +# - Copy for push request +# - Disable compression (non-zero) + +__DATA__ +1 0 0 0 0 0 0 :authority +2 0 0 0 0 0 0 :method GET +3 0 0 0 0 0 0 :method POST +4 0 0 0 0 0 0 :path / +5 0 0 0 0 0 0 :path /index.html +6 0 0 0 0 0 0 :scheme http +7 0 0 0 0 0 0 :scheme https +8 0 0 0 0 0 0 :status 200 +9 0 0 0 0 0 0 :status 204 +10 0 0 0 0 0 0 :status 206 +11 0 0 0 0 0 0 :status 304 +12 0 0 0 0 0 0 :status 400 +13 0 0 0 0 0 0 :status 404 +14 0 0 0 0 0 0 :status 500 +15 0 0 0 0 1 0 accept-charset +16 0 0 0 0 1 0 accept-encoding gzip, deflate +17 0 0 0 0 1 0 accept-language +18 0 0 0 0 0 0 accept-ranges +19 0 0 0 0 1 0 accept +20 0 0 0 0 0 0 access-control-allow-origin +21 0 0 0 0 0 0 age +22 0 0 0 0 0 0 allow +23 0 0 0 0 0 0 authorization +24 0 0 0 0 0 0 cache-control +25 0 0 0 0 0 0 content-disposition +26 0 0 0 0 0 0 content-encoding +27 0 0 0 0 0 0 content-language +28 0 0 1 0 0 0 content-length +29 0 0 0 0 0 0 content-location +30 0 0 0 0 0 0 content-range +31 0 0 0 0 0 0 content-type +32 0 0 0 0 0 1 cookie +33 0 1 0 0 0 0 date +34 0 0 0 0 0 0 etag +35 0 0 1 0 0 0 expect +36 0 0 0 0 0 0 expires +37 0 0 0 0 0 0 from +38 0 0 1 1 0 0 host +39 0 0 0 0 0 0 if-match +40 0 0 0 0 0 0 if-modified-since +41 0 0 0 0 0 0 if-none-match +42 0 0 0 0 0 0 if-range +43 0 0 0 0 0 0 if-unmodified-since +44 0 0 0 0 0 0 last-modified +45 0 0 0 0 0 0 link +46 0 0 0 0 0 0 location +47 0 0 0 0 0 0 max-forwards +48 1 0 0 0 0 0 proxy-authenticate +49 1 0 0 0 0 0 proxy-authorization +50 0 0 0 0 0 0 range +51 0 0 0 0 0 0 referer +52 0 0 0 0 0 0 refresh +53 0 0 0 0 0 0 retry-after +54 0 0 0 0 0 0 server +55 0 0 0 0 0 1 set-cookie +56 0 0 0 0 0 0 strict-transport-security +57 1 1 1 1 0 0 transfer-encoding +58 0 0 0 0 1 0 user-agent +59 0 0 0 0 0 0 vary +60 0 0 0 0 0 0 via +61 0 0 0 0 0 0 www-authenticate +0 1 1 0 1 0 0 connection +0 0 0 0 0 0 0 x-reproxy-url +0 1 1 1 1 0 0 upgrade +0 1 0 0 1 0 0 http2-settings +0 1 0 0 1 0 0 te +0 1 1 0 0 0 0 keep-alive +0 0 0 0 0 0 0 x-forwarded-for +0 0 0 0 0 0 0 x-traffic +0 0 0 0 0 0 0 cache-digest +0 0 0 0 0 0 0 x-compress-hint -- cgit v1.2.3