diff --git a/BUILD b/BUILD index fe9bcff5150..070d7472651 100644 --- a/BUILD +++ b/BUILD @@ -6809,6 +6809,28 @@ grpc_cc_library( ], ) +grpc_cc_library( + name = "huffsyms", + srcs = [ + "src/core/ext/transport/chttp2/transport/huffsyms.cc", + ], + hdrs = [ + "src/core/ext/transport/chttp2/transport/huffsyms.h", + ], + deps = ["gpr_platform"], +) + +grpc_cc_library( + name = "decode_huff", + srcs = [ + "src/core/ext/transport/chttp2/transport/decode_huff.cc", + ], + hdrs = [ + "src/core/ext/transport/chttp2/transport/decode_huff.h", + ], + deps = ["gpr_platform"], +) + grpc_cc_library( name = "http2_settings", srcs = [ @@ -6840,7 +6862,6 @@ grpc_cc_library( "src/core/ext/transport/chttp2/transport/hpack_encoder.cc", "src/core/ext/transport/chttp2/transport/hpack_parser.cc", "src/core/ext/transport/chttp2/transport/hpack_parser_table.cc", - "src/core/ext/transport/chttp2/transport/huffsyms.cc", "src/core/ext/transport/chttp2/transport/parsing.cc", "src/core/ext/transport/chttp2/transport/stream_lists.cc", "src/core/ext/transport/chttp2/transport/stream_map.cc", @@ -6862,7 +6883,6 @@ grpc_cc_library( "src/core/ext/transport/chttp2/transport/hpack_encoder.h", "src/core/ext/transport/chttp2/transport/hpack_parser.h", "src/core/ext/transport/chttp2/transport/hpack_parser_table.h", - "src/core/ext/transport/chttp2/transport/huffsyms.h", "src/core/ext/transport/chttp2/transport/internal.h", "src/core/ext/transport/chttp2/transport/stream_map.h", "src/core/ext/transport/chttp2/transport/varint.h", @@ -6885,6 +6905,7 @@ grpc_cc_library( "bitset", "chttp2_flow_control", "debug_location", + "decode_huff", "experiments", "gpr", "gpr_atm", @@ -6897,6 +6918,7 @@ grpc_cc_library( "http2_errors", "http2_settings", "httpcli", + "huffsyms", "iomgr_fwd", "iomgr_timer", "memory_quota", diff --git a/CMakeLists.txt b/CMakeLists.txt index d8b3274ddfc..a478579336b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -563,7 +563,6 @@ add_custom_target(tools_c add_custom_target(tools_cxx DEPENDS - gen_hpack_tables ) add_custom_target(tools @@ -1751,6 +1750,7 @@ add_library(grpc src/core/ext/transport/chttp2/transport/bin_encoder.cc src/core/ext/transport/chttp2/transport/chttp2_transport.cc src/core/ext/transport/chttp2/transport/context_list.cc + src/core/ext/transport/chttp2/transport/decode_huff.cc src/core/ext/transport/chttp2/transport/flow_control.cc src/core/ext/transport/chttp2/transport/frame_data.cc src/core/ext/transport/chttp2/transport/frame_goaway.cc @@ -2650,6 +2650,7 @@ add_library(grpc_unsecure src/core/ext/transport/chttp2/transport/bin_encoder.cc src/core/ext/transport/chttp2/transport/chttp2_transport.cc src/core/ext/transport/chttp2/transport/context_list.cc + src/core/ext/transport/chttp2/transport/decode_huff.cc src/core/ext/transport/chttp2/transport/flow_control.cc src/core/ext/transport/chttp2/transport/frame_data.cc src/core/ext/transport/chttp2/transport/frame_goaway.cc @@ -4293,34 +4294,6 @@ if(gRPC_INSTALL) endif() - -add_executable(gen_hpack_tables - tools/codegen/core/gen_hpack_tables.cc -) - -target_include_directories(gen_hpack_tables - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} - ${_gRPC_RE2_INCLUDE_DIR} - ${_gRPC_SSL_INCLUDE_DIR} - ${_gRPC_UPB_GENERATED_DIR} - ${_gRPC_UPB_GRPC_GENERATED_DIR} - ${_gRPC_UPB_INCLUDE_DIR} - ${_gRPC_XXHASH_INCLUDE_DIR} - ${_gRPC_ZLIB_INCLUDE_DIR} - ${_gRPC_PROTO_GENS_DIR} -) - -target_link_libraries(gen_hpack_tables - ${_gRPC_PROTOBUF_LIBRARIES} - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc - gpr -) - - if(gRPC_BUILD_TESTS) add_executable(bad_server_response_test diff --git a/Makefile b/Makefile index 365d628f920..96a4a6f535b 100644 --- a/Makefile +++ b/Makefile @@ -1036,6 +1036,7 @@ LIBGRPC_SRC = \ src/core/ext/transport/chttp2/transport/bin_encoder.cc \ src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ src/core/ext/transport/chttp2/transport/context_list.cc \ + src/core/ext/transport/chttp2/transport/decode_huff.cc \ src/core/ext/transport/chttp2/transport/flow_control.cc \ src/core/ext/transport/chttp2/transport/frame_data.cc \ src/core/ext/transport/chttp2/transport/frame_goaway.cc \ @@ -1799,6 +1800,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/transport/chttp2/transport/bin_encoder.cc \ src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ src/core/ext/transport/chttp2/transport/context_list.cc \ + src/core/ext/transport/chttp2/transport/decode_huff.cc \ src/core/ext/transport/chttp2/transport/flow_control.cc \ src/core/ext/transport/chttp2/transport/frame_data.cc \ src/core/ext/transport/chttp2/transport/frame_goaway.cc \ diff --git a/bazel/experiments.bzl b/bazel/experiments.bzl index af887ec9b63..0ed20913e37 100644 --- a/bazel/experiments.bzl +++ b/bazel/experiments.bzl @@ -20,6 +20,9 @@ EXPERIMENTS = { "dbg": { }, "off": { + "core_end2end_tests": [ + "new_hpack_huffman_decoder", + ], "endpoint_test": [ "tcp_frame_size_tuning", "tcp_rcv_lowat", @@ -32,6 +35,9 @@ EXPERIMENTS = { "tcp_rcv_lowat", "tcp_read_chunks", ], + "hpack_test": [ + "new_hpack_huffman_decoder", + ], "resource_quota_test": [ "memory_pressure_controller", "periodic_resource_quota_reclamation", diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 5151e15224c..389e017985f 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -373,6 +373,7 @@ libs: - src/core/ext/transport/chttp2/transport/bin_encoder.h - src/core/ext/transport/chttp2/transport/chttp2_transport.h - src/core/ext/transport/chttp2/transport/context_list.h + - src/core/ext/transport/chttp2/transport/decode_huff.h - src/core/ext/transport/chttp2/transport/flow_control.h - src/core/ext/transport/chttp2/transport/frame.h - src/core/ext/transport/chttp2/transport/frame_data.h @@ -1089,6 +1090,7 @@ libs: - src/core/ext/transport/chttp2/transport/bin_encoder.cc - src/core/ext/transport/chttp2/transport/chttp2_transport.cc - src/core/ext/transport/chttp2/transport/context_list.cc + - src/core/ext/transport/chttp2/transport/decode_huff.cc - src/core/ext/transport/chttp2/transport/flow_control.cc - src/core/ext/transport/chttp2/transport/frame_data.cc - src/core/ext/transport/chttp2/transport/frame_goaway.cc @@ -1863,6 +1865,7 @@ libs: - src/core/ext/transport/chttp2/transport/bin_encoder.h - src/core/ext/transport/chttp2/transport/chttp2_transport.h - src/core/ext/transport/chttp2/transport/context_list.h + - src/core/ext/transport/chttp2/transport/decode_huff.h - src/core/ext/transport/chttp2/transport/flow_control.h - src/core/ext/transport/chttp2/transport/frame.h - src/core/ext/transport/chttp2/transport/frame_data.h @@ -2217,6 +2220,7 @@ libs: - src/core/ext/transport/chttp2/transport/bin_encoder.cc - src/core/ext/transport/chttp2/transport/chttp2_transport.cc - src/core/ext/transport/chttp2/transport/context_list.cc + - src/core/ext/transport/chttp2/transport/decode_huff.cc - src/core/ext/transport/chttp2/transport/flow_control.cc - src/core/ext/transport/chttp2/transport/frame_data.cc - src/core/ext/transport/chttp2/transport/frame_goaway.cc diff --git a/build_handwritten.yaml b/build_handwritten.yaml index 195d06b0e9b..aadc59ce651 100644 --- a/build_handwritten.yaml +++ b/build_handwritten.yaml @@ -17,16 +17,6 @@ settings: g_stands_for: galley protobuf_version: 3.21.5 version: 1.50.0-dev -targets: -- name: gen_hpack_tables - build: tool - language: c++ - src: - - tools/codegen/core/gen_hpack_tables.cc - deps: - - grpc - - gpr - uses_polling: false configs: asan: CC: clang diff --git a/config.m4 b/config.m4 index 5d40a27b21c..d4d627f2aaa 100644 --- a/config.m4 +++ b/config.m4 @@ -118,6 +118,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/transport/chttp2/transport/bin_encoder.cc \ src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ src/core/ext/transport/chttp2/transport/context_list.cc \ + src/core/ext/transport/chttp2/transport/decode_huff.cc \ src/core/ext/transport/chttp2/transport/flow_control.cc \ src/core/ext/transport/chttp2/transport/frame_data.cc \ src/core/ext/transport/chttp2/transport/frame_goaway.cc \ diff --git a/config.w32 b/config.w32 index 41a933b94d9..9bc4a792503 100644 --- a/config.w32 +++ b/config.w32 @@ -84,6 +84,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\transport\\chttp2\\transport\\bin_encoder.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\chttp2_transport.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\context_list.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\decode_huff.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\flow_control.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\frame_data.cc " + "src\\core\\ext\\transport\\chttp2\\transport\\frame_goaway.cc " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index c673d33cb16..91442fa917f 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -321,6 +321,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/bin_encoder.h', 'src/core/ext/transport/chttp2/transport/chttp2_transport.h', 'src/core/ext/transport/chttp2/transport/context_list.h', + 'src/core/ext/transport/chttp2/transport/decode_huff.h', 'src/core/ext/transport/chttp2/transport/flow_control.h', 'src/core/ext/transport/chttp2/transport/frame.h', 'src/core/ext/transport/chttp2/transport/frame_data.h', @@ -1182,6 +1183,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/bin_encoder.h', 'src/core/ext/transport/chttp2/transport/chttp2_transport.h', 'src/core/ext/transport/chttp2/transport/context_list.h', + 'src/core/ext/transport/chttp2/transport/decode_huff.h', 'src/core/ext/transport/chttp2/transport/flow_control.h', 'src/core/ext/transport/chttp2/transport/frame.h', 'src/core/ext/transport/chttp2/transport/frame_data.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 689790071b3..aa425e9e69e 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -337,6 +337,8 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/chttp2_transport.h', 'src/core/ext/transport/chttp2/transport/context_list.cc', 'src/core/ext/transport/chttp2/transport/context_list.h', + 'src/core/ext/transport/chttp2/transport/decode_huff.cc', + 'src/core/ext/transport/chttp2/transport/decode_huff.h', 'src/core/ext/transport/chttp2/transport/flow_control.cc', 'src/core/ext/transport/chttp2/transport/flow_control.h', 'src/core/ext/transport/chttp2/transport/frame.h', @@ -1809,6 +1811,7 @@ Pod::Spec.new do |s| 'src/core/ext/transport/chttp2/transport/bin_encoder.h', 'src/core/ext/transport/chttp2/transport/chttp2_transport.h', 'src/core/ext/transport/chttp2/transport/context_list.h', + 'src/core/ext/transport/chttp2/transport/decode_huff.h', 'src/core/ext/transport/chttp2/transport/flow_control.h', 'src/core/ext/transport/chttp2/transport/frame.h', 'src/core/ext/transport/chttp2/transport/frame_data.h', diff --git a/grpc.gemspec b/grpc.gemspec index 44d000c7c11..ffab9a365cb 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -250,6 +250,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/transport/chttp2/transport/chttp2_transport.h ) s.files += %w( src/core/ext/transport/chttp2/transport/context_list.cc ) s.files += %w( src/core/ext/transport/chttp2/transport/context_list.h ) + s.files += %w( src/core/ext/transport/chttp2/transport/decode_huff.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/decode_huff.h ) s.files += %w( src/core/ext/transport/chttp2/transport/flow_control.cc ) s.files += %w( src/core/ext/transport/chttp2/transport/flow_control.h ) s.files += %w( src/core/ext/transport/chttp2/transport/frame.h ) diff --git a/grpc.gyp b/grpc.gyp index 2178d392449..749adf1981d 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -449,6 +449,7 @@ 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', 'src/core/ext/transport/chttp2/transport/context_list.cc', + 'src/core/ext/transport/chttp2/transport/decode_huff.cc', 'src/core/ext/transport/chttp2/transport/flow_control.cc', 'src/core/ext/transport/chttp2/transport/frame_data.cc', 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', @@ -1190,6 +1191,7 @@ 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', 'src/core/ext/transport/chttp2/transport/context_list.cc', + 'src/core/ext/transport/chttp2/transport/decode_huff.cc', 'src/core/ext/transport/chttp2/transport/flow_control.cc', 'src/core/ext/transport/chttp2/transport/frame_data.cc', 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', diff --git a/package.xml b/package.xml index 98a87277790..c70129bb2f1 100644 --- a/package.xml +++ b/package.xml @@ -232,6 +232,8 @@ + + diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.cc b/src/core/ext/transport/chttp2/transport/bin_encoder.cc index eacf9ab9635..248d89c1da2 100644 --- a/src/core/ext/transport/chttp2/transport/bin_encoder.cc +++ b/src/core/ext/transport/chttp2/transport/bin_encoder.cc @@ -98,7 +98,7 @@ grpc_slice grpc_chttp2_huffman_compress(const grpc_slice& input) { const uint8_t* in; uint8_t* out; grpc_slice output; - uint32_t temp = 0; + uint64_t temp = 0; uint32_t temp_length = 0; nbits = 0; diff --git a/src/core/ext/transport/chttp2/transport/decode_huff.cc b/src/core/ext/transport/chttp2/transport/decode_huff.cc new file mode 100644 index 00000000000..677016b092f --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/decode_huff.cc @@ -0,0 +1,287 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//////////////////////////////////////////////////////////////////////////////// +// This file is autogenerated: see +// tools/codegen/core/gen_huffman_decompressor.cc + +#include + +#include "src/core/ext/transport/chttp2/transport/decode_huff.h" +namespace grpc_core { +const uint8_t HuffDecoderCommon::table2_0_emit_[10] = { + 0x30, 0x31, 0x32, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x73, 0x74}; +const uint8_t HuffDecoderCommon::table2_0_ops_[32] = { + 0x02, 0x06, 0x0a, 0x0e, 0x12, 0x16, 0x1a, 0x1e, 0x22, 0x26, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table3_0_emit_[36] = { + 0x30, 0x31, 0x32, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x73, 0x74, 0x20, 0x25, + 0x2d, 0x2e, 0x2f, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3d, 0x41, + 0x5f, 0x62, 0x64, 0x66, 0x67, 0x68, 0x6c, 0x6d, 0x6e, 0x70, 0x72, 0x75}; +const uint8_t HuffDecoderCommon::table3_0_ops_[64] = { + 0x01, 0x02, 0x01, 0x06, 0x01, 0x0a, 0x01, 0x0e, 0x01, 0x12, 0x01, + 0x16, 0x01, 0x1a, 0x01, 0x1e, 0x01, 0x22, 0x01, 0x26, 0x2a, 0x2e, + 0x32, 0x36, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x52, 0x56, 0x5a, + 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76, 0x7a, 0x7e, 0x82, 0x86, + 0x8a, 0x8e, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table4_0_emit_[22] = { + 0x30, 0x31, 0x32, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x73, 0x74, 0x20, + 0x25, 0x2d, 0x2e, 0x2f, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39}; +const uint8_t HuffDecoderCommon::table4_0_ops_[64] = { + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x06, 0x01, 0x01, 0x01, + 0x0a, 0x01, 0x01, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x12, 0x01, 0x01, + 0x01, 0x16, 0x01, 0x01, 0x01, 0x1a, 0x01, 0x01, 0x01, 0x1e, 0x01, + 0x01, 0x01, 0x22, 0x01, 0x01, 0x01, 0x26, 0x01, 0x2a, 0x01, 0x2e, + 0x01, 0x32, 0x01, 0x36, 0x01, 0x3a, 0x01, 0x3e, 0x01, 0x42, 0x01, + 0x46, 0x01, 0x4a, 0x01, 0x4e, 0x01, 0x52, 0x01, 0x56}; +const uint8_t HuffDecoderCommon::table4_1_emit_[46] = { + 0x3d, 0x41, 0x5f, 0x62, 0x64, 0x66, 0x67, 0x68, 0x6c, 0x6d, 0x6e, 0x70, + 0x72, 0x75, 0x3a, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x59, 0x6a, 0x6b, 0x71, 0x76, 0x77, 0x78, 0x79, 0x7a}; +const uint8_t HuffDecoderCommon::table4_1_ops_[64] = { + 0x01, 0x02, 0x01, 0x06, 0x01, 0x0a, 0x01, 0x0e, 0x01, 0x12, 0x01, + 0x16, 0x01, 0x1a, 0x01, 0x1e, 0x01, 0x22, 0x01, 0x26, 0x01, 0x2a, + 0x01, 0x2e, 0x01, 0x32, 0x01, 0x36, 0x3a, 0x3e, 0x42, 0x46, 0x4a, + 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76, + 0x7a, 0x7e, 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e, 0xa2, + 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0x01, 0x01, 0x01, 0x00}; +const uint8_t* const HuffDecoderCommon::table4_emit_[2] = { + table4_0_emit_, + table4_1_emit_, +}; +const uint8_t* const HuffDecoderCommon::table4_ops_[2] = { + table4_0_ops_, + table4_1_ops_, +}; +const uint8_t HuffDecoderCommon::table1_0_emit_[74] = { + 0x30, 0x31, 0x32, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x73, 0x74, 0x20, + 0x25, 0x2d, 0x2e, 0x2f, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3d, 0x41, 0x5f, 0x62, 0x64, 0x66, 0x67, 0x68, 0x6c, 0x6d, 0x6e, + 0x70, 0x72, 0x75, 0x3a, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x59, 0x6a, 0x6b, 0x71, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x26, 0x2a, 0x2c, 0x3b, 0x58, 0x5a}; +const uint16_t HuffDecoderCommon::table1_0_inner_[76] = { + 0x0005, 0x0045, 0x0085, 0x00c5, 0x0105, 0x0145, 0x0185, 0x01c5, 0x0205, + 0x0245, 0x0286, 0x02c6, 0x0306, 0x0346, 0x0386, 0x03c6, 0x0406, 0x0446, + 0x0486, 0x04c6, 0x0506, 0x0546, 0x0586, 0x05c6, 0x0606, 0x0646, 0x0686, + 0x06c6, 0x0706, 0x0746, 0x0786, 0x07c6, 0x0806, 0x0846, 0x0886, 0x08c6, + 0x0907, 0x0947, 0x0987, 0x09c7, 0x0a07, 0x0a47, 0x0a87, 0x0ac7, 0x0b07, + 0x0b47, 0x0b87, 0x0bc7, 0x0c07, 0x0c47, 0x0c87, 0x0cc7, 0x0d07, 0x0d47, + 0x0d87, 0x0dc7, 0x0e07, 0x0e47, 0x0e87, 0x0ec7, 0x0f07, 0x0f47, 0x0f87, + 0x0fc7, 0x1007, 0x1047, 0x1087, 0x10c7, 0x1108, 0x1148, 0x1188, 0x11c8, + 0x1208, 0x1248, 0x0018, 0x0028}; +const uint8_t HuffDecoderCommon::table1_0_outer_[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, + 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, + 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, + 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, + 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 38, 38, + 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, + 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, + 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, 67, + 67, 68, 69, 70, 71, 72, 73, 74, 75}; +const uint8_t HuffDecoderCommon::table5_0_emit_[4] = {0x21, 0x22, 0x28, 0x29}; +const uint8_t HuffDecoderCommon::table5_0_inner_[4] = {0x02, 0x06, 0x0a, 0x0e}; +const uint8_t HuffDecoderCommon::table5_0_outer_[4] = {0, 1, 2, 3}; +const uint8_t HuffDecoderCommon::table7_0_emit_[1] = {0x3f}; +const uint8_t HuffDecoderCommon::table7_0_inner_[3] = {0x02, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table7_0_outer_[4] = {0, 1, 1, 2}; +const uint8_t HuffDecoderCommon::table8_0_emit_[4] = {0x3f, 0x27, 0x2b, 0x7c}; +const uint8_t HuffDecoderCommon::table8_0_inner_[6] = {0x01, 0x02, 0x06, + 0x0a, 0x0e, 0x00}; +const uint8_t HuffDecoderCommon::table8_0_outer_[8] = {0, 1, 2, 3, 4, 0, 0, 5}; +const uint8_t HuffDecoderCommon::table9_0_emit_[6] = {0x3f, 0x27, 0x2b, + 0x7c, 0x23, 0x3e}; +const uint8_t HuffDecoderCommon::table9_0_inner_[8] = {0x01, 0x02, 0x06, 0x0a, + 0x0e, 0x12, 0x16, 0x00}; +const uint8_t HuffDecoderCommon::table9_0_outer_[16] = {0, 0, 0, 1, 0, 2, 0, 3, + 0, 4, 5, 6, 0, 0, 0, 7}; +const uint8_t HuffDecoderCommon::table10_0_emit_[12] = { + 0x3f, 0x27, 0x2b, 0x7c, 0x23, 0x3e, 0x00, 0x24, 0x40, 0x5b, 0x5d, 0x7e}; +const uint8_t HuffDecoderCommon::table10_0_ops_[32] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x06, 0x01, 0x01, 0x01, 0x0a, 0x01, 0x01, 0x01, 0x0e, 0x01, 0x12, + 0x01, 0x16, 0x1a, 0x1e, 0x22, 0x26, 0x2a, 0x2e, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table11_0_emit_[14] = { + 0x3f, 0x27, 0x2b, 0x7c, 0x23, 0x3e, 0x00, + 0x24, 0x40, 0x5b, 0x5d, 0x7e, 0x5e, 0x7d}; +const uint8_t HuffDecoderCommon::table11_0_ops_[64] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0a, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x12, + 0x01, 0x01, 0x01, 0x16, 0x01, 0x1a, 0x01, 0x1e, 0x01, 0x22, 0x01, + 0x26, 0x01, 0x2a, 0x01, 0x2e, 0x32, 0x36, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table6_0_emit_[3] = {0x3f, 0x27, 0x2b}; +const uint8_t HuffDecoderCommon::table6_0_ops_[64] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23}; +const uint8_t HuffDecoderCommon::table6_1_emit_[14] = { + 0x7c, 0x23, 0x3e, 0x00, 0x24, 0x40, 0x5b, + 0x5d, 0x7e, 0x5e, 0x7d, 0x3c, 0x60, 0x7b}; +const uint8_t HuffDecoderCommon::table6_1_ops_[64] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x35, + 0x35, 0x35, 0x35, 0x45, 0x45, 0x45, 0x45, 0x55, 0x55, 0x55, 0x55, + 0x65, 0x65, 0x65, 0x65, 0x75, 0x75, 0x75, 0x75, 0x85, 0x85, 0x85, + 0x85, 0x96, 0x96, 0xa6, 0xa6, 0xb7, 0xc7, 0xd7, 0x0f}; +const uint8_t* const HuffDecoderCommon::table6_emit_[2] = { + table6_0_emit_, + table6_1_emit_, +}; +const uint8_t* const HuffDecoderCommon::table6_ops_[2] = { + table6_0_ops_, + table6_1_ops_, +}; +const uint8_t HuffDecoderCommon::table13_0_emit_[3] = {0x5c, 0xc3, 0xd0}; +const uint8_t HuffDecoderCommon::table13_0_inner_[5] = {0x02, 0x06, 0x0a, 0x01, + 0x00}; +const uint8_t HuffDecoderCommon::table13_0_outer_[16] = { + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4}; +const uint8_t HuffDecoderCommon::table14_0_emit_[11] = { + 0x5c, 0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2, 0xe0, 0xe2}; +const uint8_t HuffDecoderCommon::table14_0_ops_[32] = { + 0x01, 0x02, 0x01, 0x06, 0x01, 0x0a, 0x0e, 0x12, 0x16, 0x1a, 0x1e, + 0x22, 0x26, 0x2a, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table15_0_emit_[24] = { + 0x5c, 0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2, 0xe0, 0xe2, 0x99, + 0xa1, 0xa7, 0xac, 0xb0, 0xb1, 0xb3, 0xd1, 0xd8, 0xd9, 0xe3, 0xe5, 0xe6}; +const uint8_t HuffDecoderCommon::table15_0_ops_[64] = { + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x06, 0x01, 0x01, 0x01, + 0x0a, 0x01, 0x0e, 0x01, 0x12, 0x01, 0x16, 0x01, 0x1a, 0x01, 0x1e, + 0x01, 0x22, 0x01, 0x26, 0x01, 0x2a, 0x2e, 0x32, 0x36, 0x3a, 0x3e, + 0x42, 0x46, 0x4a, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table16_0_emit_[50] = { + 0x5c, 0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2, 0xe0, + 0xe2, 0x99, 0xa1, 0xa7, 0xac, 0xb0, 0xb1, 0xb3, 0xd1, 0xd8, + 0xd9, 0xe3, 0xe5, 0xe6, 0x81, 0x84, 0x85, 0x86, 0x88, 0x92, + 0x9a, 0x9c, 0xa0, 0xa3, 0xa4, 0xa9, 0xaa, 0xad, 0xb2, 0xb5, + 0xb9, 0xba, 0xbb, 0xbd, 0xbe, 0xc4, 0xc6, 0xe4, 0xe8, 0xe9}; +const uint8_t HuffDecoderCommon::table16_0_ops_[128] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0a, + 0x01, 0x01, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x12, 0x01, 0x01, 0x01, 0x16, + 0x01, 0x01, 0x01, 0x1a, 0x01, 0x01, 0x01, 0x1e, 0x01, 0x01, 0x01, 0x22, + 0x01, 0x01, 0x01, 0x26, 0x01, 0x01, 0x01, 0x2a, 0x01, 0x2e, 0x01, 0x32, + 0x01, 0x36, 0x01, 0x3a, 0x01, 0x3e, 0x01, 0x42, 0x01, 0x46, 0x01, 0x4a, + 0x01, 0x4e, 0x01, 0x52, 0x01, 0x56, 0x01, 0x5a, 0x01, 0x5e, 0x62, 0x66, + 0x6a, 0x6e, 0x72, 0x76, 0x7a, 0x7e, 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, + 0x9a, 0x9e, 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe, 0xc2, 0xc6, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table12_0_emit_[79] = { + 0x5c, 0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2, 0xe0, 0xe2, 0x99, + 0xa1, 0xa7, 0xac, 0xb0, 0xb1, 0xb3, 0xd1, 0xd8, 0xd9, 0xe3, 0xe5, 0xe6, + 0x81, 0x84, 0x85, 0x86, 0x88, 0x92, 0x9a, 0x9c, 0xa0, 0xa3, 0xa4, 0xa9, + 0xaa, 0xad, 0xb2, 0xb5, 0xb9, 0xba, 0xbb, 0xbd, 0xbe, 0xc4, 0xc6, 0xe4, + 0xe8, 0xe9, 0x01, 0x87, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8f, 0x93, 0x95, + 0x96, 0x97, 0x98, 0x9b, 0x9d, 0x9e, 0xa5, 0xa6, 0xa8, 0xae, 0xaf, 0xb4, + 0xb6, 0xb7, 0xbc, 0xbf, 0xc5, 0xe7, 0xef}; +const uint16_t HuffDecoderCommon::table12_0_inner_[90] = { + 0x0004, 0x0104, 0x0204, 0x0305, 0x0405, 0x0505, 0x0605, 0x0705, 0x0805, + 0x0905, 0x0a05, 0x0b06, 0x0c06, 0x0d06, 0x0e06, 0x0f06, 0x1006, 0x1106, + 0x1206, 0x1306, 0x1406, 0x1506, 0x1606, 0x1706, 0x1807, 0x1907, 0x1a07, + 0x1b07, 0x1c07, 0x1d07, 0x1e07, 0x1f07, 0x2007, 0x2107, 0x2207, 0x2307, + 0x2407, 0x2507, 0x2607, 0x2707, 0x2807, 0x2907, 0x2a07, 0x2b07, 0x2c07, + 0x2d07, 0x2e07, 0x2f07, 0x3007, 0x3107, 0x3208, 0x3308, 0x3408, 0x3508, + 0x3608, 0x3708, 0x3808, 0x3908, 0x3a08, 0x3b08, 0x3c08, 0x3d08, 0x3e08, + 0x3f08, 0x4008, 0x4108, 0x4208, 0x4308, 0x4408, 0x4508, 0x4608, 0x4708, + 0x4808, 0x4908, 0x4a08, 0x4b08, 0x4c08, 0x4d08, 0x4e08, 0x0018, 0x0028, + 0x0038, 0x0048, 0x0058, 0x0068, 0x0078, 0x0088, 0x0098, 0x00a8, 0x00b8}; +const uint8_t HuffDecoderCommon::table12_0_outer_[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, + 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, + 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, + 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 25, 25, 26, 26, 27, + 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, + 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, + 46, 47, 47, 48, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89}; +const uint8_t HuffDecoderCommon::table17_0_emit_[2] = {0x09, 0x8e}; +const uint8_t HuffDecoderCommon::table17_0_inner_[2] = {0x01, 0x03}; +const uint8_t HuffDecoderCommon::table17_0_outer_[2] = {0, 1}; +const uint8_t HuffDecoderCommon::table18_0_emit_[2] = {0x90, 0x91}; +const uint8_t HuffDecoderCommon::table18_0_inner_[2] = {0x01, 0x03}; +const uint8_t HuffDecoderCommon::table18_0_outer_[2] = {0, 1}; +const uint8_t HuffDecoderCommon::table19_0_emit_[2] = {0x94, 0x9f}; +const uint8_t HuffDecoderCommon::table20_0_emit_[2] = {0xab, 0xce}; +const uint8_t HuffDecoderCommon::table21_0_emit_[2] = {0xd7, 0xe1}; +const uint8_t HuffDecoderCommon::table22_0_emit_[2] = {0xec, 0xed}; +const uint8_t HuffDecoderCommon::table23_0_emit_[4] = {0xc7, 0xcf, 0xea, 0xeb}; +const uint8_t HuffDecoderCommon::table23_0_outer_[4] = {0, 1, 2, 3}; +const uint8_t HuffDecoderCommon::table24_0_emit_[8] = {0xc0, 0xc1, 0xc8, 0xc9, + 0xca, 0xcd, 0xd2, 0xd5}; +const uint8_t HuffDecoderCommon::table24_0_inner_[8] = {0x03, 0x07, 0x0b, 0x0f, + 0x13, 0x17, 0x1b, 0x1f}; +const uint8_t HuffDecoderCommon::table24_0_outer_[8] = {0, 1, 2, 3, 4, 5, 6, 7}; +const uint8_t HuffDecoderCommon::table25_0_emit_[16] = { + 0xd3, 0xd4, 0xd6, 0xdd, 0xde, 0xdf, 0xf1, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd}; +const uint8_t HuffDecoderCommon::table25_0_inner_[16] = { + 0x04, 0x0c, 0x14, 0x1c, 0x24, 0x2c, 0x34, 0x3c, + 0x44, 0x4c, 0x54, 0x5c, 0x64, 0x6c, 0x74, 0x7c}; +const uint8_t HuffDecoderCommon::table25_0_outer_[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; +const uint8_t HuffDecoderCommon::table27_0_emit_[1] = {0xfe}; +const uint8_t HuffDecoderCommon::table27_0_inner_[3] = {0x02, 0x01, 0x00}; +const uint8_t HuffDecoderCommon::table27_0_outer_[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}; +const uint8_t HuffDecoderCommon::table26_0_emit_[30] = { + 0xfe, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0b, 0x0c, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x7f, 0xdc, 0xf9}; +const uint16_t HuffDecoderCommon::table26_0_inner_[31] = { + 0x0004, 0x0015, 0x0025, 0x0035, 0x0045, 0x0055, 0x0065, 0x0075, + 0x0085, 0x0095, 0x00a5, 0x00b5, 0x00c5, 0x00d5, 0x00e5, 0x00f5, + 0x0105, 0x0115, 0x0125, 0x0135, 0x0145, 0x0155, 0x0165, 0x0175, + 0x0185, 0x0195, 0x01a5, 0x01b5, 0x01c5, 0x01d5, 0x000d}; +const uint8_t HuffDecoderCommon::table26_0_outer_[32] = { + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; +const uint8_t HuffDecoderCommon::table28_0_emit_[3] = {0x0a, 0x0d, 0x16}; +const uint8_t HuffDecoderCommon::table28_0_inner_[4] = {0x02, 0x0a, 0x12, 0x06}; +const uint8_t HuffDecoderCommon::table30_0_emit_[7] = {0xda, 0xdb, 0xee, 0xf0, + 0xf2, 0xf3, 0xff}; +const uint8_t HuffDecoderCommon::table30_0_inner_[8] = {0x02, 0x06, 0x0a, 0x0e, + 0x12, 0x16, 0x1a, 0x01}; +const uint8_t HuffDecoderCommon::table29_0_emit_[9] = { + 0xda, 0xdb, 0xee, 0xf0, 0xf2, 0xf3, 0xff, 0xcb, 0xcc}; +const uint8_t HuffDecoderCommon::table29_0_inner_[9] = { + 0x03, 0x0b, 0x13, 0x1b, 0x23, 0x2b, 0x33, 0x3c, 0x44}; +const uint8_t HuffDecoderCommon::table29_0_outer_[16] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8}; +} // namespace grpc_core diff --git a/src/core/ext/transport/chttp2/transport/decode_huff.h b/src/core/ext/transport/chttp2/transport/decode_huff.h new file mode 100644 index 00000000000..6fb13efd297 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/decode_huff.h @@ -0,0 +1,1018 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//////////////////////////////////////////////////////////////////////////////// +// This file is autogenerated: see +// tools/codegen/core/gen_huffman_decompressor.cc + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_DECODE_HUFF_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_DECODE_HUFF_H +#include + +#include +#include +// GEOMETRY: 8,7,8,5 +namespace grpc_core { +class HuffDecoderCommon { + protected: + static inline uint64_t GetOp2(size_t i) { return table2_0_ops_[i]; } + static inline uint64_t GetEmit2(size_t, size_t emit) { + return table2_0_emit_[emit]; + } + static inline uint64_t GetOp3(size_t i) { return table3_0_ops_[i]; } + static inline uint64_t GetEmit3(size_t, size_t emit) { + return table3_0_emit_[emit]; + } + static inline uint64_t GetOp4(size_t i) { + return table4_ops_[i >> 6][i & 0x3f]; + } + static inline uint64_t GetEmit4(size_t i, size_t emit) { + return table4_emit_[i >> 6][emit]; + } + static inline uint64_t GetOp1(size_t i) { + return table1_0_inner_[table1_0_outer_[i]]; + } + static inline uint64_t GetEmit1(size_t, size_t emit) { + return table1_0_emit_[emit]; + } + static inline uint64_t GetOp5(size_t i) { + return table5_0_inner_[table5_0_outer_[i]]; + } + static inline uint64_t GetEmit5(size_t, size_t emit) { + return table5_0_emit_[emit]; + } + static inline uint64_t GetOp7(size_t i) { + return table7_0_inner_[table7_0_outer_[i]]; + } + static inline uint64_t GetEmit7(size_t, size_t emit) { + return table7_0_emit_[emit]; + } + static inline uint64_t GetOp8(size_t i) { + return table8_0_inner_[table8_0_outer_[i]]; + } + static inline uint64_t GetEmit8(size_t, size_t emit) { + return table8_0_emit_[emit]; + } + static inline uint64_t GetOp9(size_t i) { + return table9_0_inner_[table9_0_outer_[i]]; + } + static inline uint64_t GetEmit9(size_t, size_t emit) { + return table9_0_emit_[emit]; + } + static inline uint64_t GetOp10(size_t i) { return table10_0_ops_[i]; } + static inline uint64_t GetEmit10(size_t, size_t emit) { + return table10_0_emit_[emit]; + } + static inline uint64_t GetOp11(size_t i) { return table11_0_ops_[i]; } + static inline uint64_t GetEmit11(size_t, size_t emit) { + return table11_0_emit_[emit]; + } + static inline uint64_t GetOp6(size_t i) { + return table6_ops_[i >> 6][i & 0x3f]; + } + static inline uint64_t GetEmit6(size_t i, size_t emit) { + return table6_emit_[i >> 6][emit]; + } + static inline uint64_t GetOp13(size_t i) { + return table13_0_inner_[table13_0_outer_[i]]; + } + static inline uint64_t GetEmit13(size_t, size_t emit) { + return table13_0_emit_[emit]; + } + static inline uint64_t GetOp14(size_t i) { return table14_0_ops_[i]; } + static inline uint64_t GetEmit14(size_t, size_t emit) { + return table14_0_emit_[emit]; + } + static inline uint64_t GetOp15(size_t i) { return table15_0_ops_[i]; } + static inline uint64_t GetEmit15(size_t, size_t emit) { + return table15_0_emit_[emit]; + } + static inline uint64_t GetOp16(size_t i) { return table16_0_ops_[i]; } + static inline uint64_t GetEmit16(size_t, size_t emit) { + return table16_0_emit_[emit]; + } + static inline uint64_t GetOp12(size_t i) { + return table12_0_inner_[table12_0_outer_[i]]; + } + static inline uint64_t GetEmit12(size_t, size_t emit) { + return table12_0_emit_[emit]; + } + static inline uint64_t GetOp17(size_t i) { + return table17_0_inner_[table17_0_outer_[i]]; + } + static inline uint64_t GetEmit17(size_t, size_t emit) { + return table17_0_emit_[emit]; + } + static inline uint64_t GetOp18(size_t i) { + return table18_0_inner_[table18_0_outer_[i]]; + } + static inline uint64_t GetEmit18(size_t, size_t emit) { + return table18_0_emit_[emit]; + } + static inline uint64_t GetOp19(size_t i) { + return table17_0_inner_[table18_0_outer_[i]]; + } + static inline uint64_t GetEmit19(size_t, size_t emit) { + return table19_0_emit_[emit]; + } + static inline uint64_t GetOp20(size_t i) { + return table17_0_inner_[table18_0_outer_[i]]; + } + static inline uint64_t GetEmit20(size_t, size_t emit) { + return table20_0_emit_[emit]; + } + static inline uint64_t GetOp21(size_t i) { + return table17_0_inner_[table18_0_outer_[i]]; + } + static inline uint64_t GetEmit21(size_t, size_t emit) { + return table21_0_emit_[emit]; + } + static inline uint64_t GetOp22(size_t i) { + return table17_0_inner_[table18_0_outer_[i]]; + } + static inline uint64_t GetEmit22(size_t, size_t emit) { + return table22_0_emit_[emit]; + } + static inline uint64_t GetOp23(size_t i) { + return table5_0_inner_[table23_0_outer_[i]]; + } + static inline uint64_t GetEmit23(size_t, size_t emit) { + return table23_0_emit_[emit]; + } + static inline uint64_t GetOp24(size_t i) { + return table24_0_inner_[table24_0_outer_[i]]; + } + static inline uint64_t GetEmit24(size_t, size_t emit) { + return table24_0_emit_[emit]; + } + static inline uint64_t GetOp25(size_t i) { + return table25_0_inner_[table25_0_outer_[i]]; + } + static inline uint64_t GetEmit25(size_t, size_t emit) { + return table25_0_emit_[emit]; + } + static inline uint64_t GetOp27(size_t i) { + return table27_0_inner_[table27_0_outer_[i]]; + } + static inline uint64_t GetEmit27(size_t, size_t emit) { + return table27_0_emit_[emit]; + } + static inline uint64_t GetOp26(size_t i) { + return table26_0_inner_[table26_0_outer_[i]]; + } + static inline uint64_t GetEmit26(size_t, size_t emit) { + return table26_0_emit_[emit]; + } + static inline uint64_t GetOp28(size_t i) { + return table28_0_inner_[table5_0_outer_[i]]; + } + static inline uint64_t GetEmit28(size_t, size_t emit) { + return table28_0_emit_[emit]; + } + static inline uint64_t GetOp30(size_t i) { + return table30_0_inner_[table24_0_outer_[i]]; + } + static inline uint64_t GetEmit30(size_t, size_t emit) { + return table30_0_emit_[emit]; + } + static inline uint64_t GetOp29(size_t i) { + return table29_0_inner_[table29_0_outer_[i]]; + } + static inline uint64_t GetEmit29(size_t, size_t emit) { + return table29_0_emit_[emit]; + } + + private: + static const uint8_t table2_0_emit_[10]; + static const uint8_t table2_0_ops_[32]; + static const uint8_t table3_0_emit_[36]; + static const uint8_t table3_0_ops_[64]; + static const uint8_t table4_0_emit_[22]; + static const uint8_t table4_0_ops_[64]; + static const uint8_t table4_1_emit_[46]; + static const uint8_t table4_1_ops_[64]; + static const uint8_t* const table4_emit_[2]; + static const uint8_t* const table4_ops_[2]; + static const uint8_t table1_0_emit_[74]; + static const uint16_t table1_0_inner_[76]; + static const uint8_t table1_0_outer_[256]; + static const uint8_t table5_0_emit_[4]; + static const uint8_t table5_0_inner_[4]; + static const uint8_t table5_0_outer_[4]; + static const uint8_t table7_0_emit_[1]; + static const uint8_t table7_0_inner_[3]; + static const uint8_t table7_0_outer_[4]; + static const uint8_t table8_0_emit_[4]; + static const uint8_t table8_0_inner_[6]; + static const uint8_t table8_0_outer_[8]; + static const uint8_t table9_0_emit_[6]; + static const uint8_t table9_0_inner_[8]; + static const uint8_t table9_0_outer_[16]; + static const uint8_t table10_0_emit_[12]; + static const uint8_t table10_0_ops_[32]; + static const uint8_t table11_0_emit_[14]; + static const uint8_t table11_0_ops_[64]; + static const uint8_t table6_0_emit_[3]; + static const uint8_t table6_0_ops_[64]; + static const uint8_t table6_1_emit_[14]; + static const uint8_t table6_1_ops_[64]; + static const uint8_t* const table6_emit_[2]; + static const uint8_t* const table6_ops_[2]; + static const uint8_t table13_0_emit_[3]; + static const uint8_t table13_0_inner_[5]; + static const uint8_t table13_0_outer_[16]; + static const uint8_t table14_0_emit_[11]; + static const uint8_t table14_0_ops_[32]; + static const uint8_t table15_0_emit_[24]; + static const uint8_t table15_0_ops_[64]; + static const uint8_t table16_0_emit_[50]; + static const uint8_t table16_0_ops_[128]; + static const uint8_t table12_0_emit_[79]; + static const uint16_t table12_0_inner_[90]; + static const uint8_t table12_0_outer_[256]; + static const uint8_t table17_0_emit_[2]; + static const uint8_t table17_0_inner_[2]; + static const uint8_t table17_0_outer_[2]; + static const uint8_t table18_0_emit_[2]; + static const uint8_t table18_0_inner_[2]; + static const uint8_t table18_0_outer_[2]; + static const uint8_t table19_0_emit_[2]; + static const uint8_t table20_0_emit_[2]; + static const uint8_t table21_0_emit_[2]; + static const uint8_t table22_0_emit_[2]; + static const uint8_t table23_0_emit_[4]; + static const uint8_t table23_0_outer_[4]; + static const uint8_t table24_0_emit_[8]; + static const uint8_t table24_0_inner_[8]; + static const uint8_t table24_0_outer_[8]; + static const uint8_t table25_0_emit_[16]; + static const uint8_t table25_0_inner_[16]; + static const uint8_t table25_0_outer_[16]; + static const uint8_t table27_0_emit_[1]; + static const uint8_t table27_0_inner_[3]; + static const uint8_t table27_0_outer_[16]; + static const uint8_t table26_0_emit_[30]; + static const uint16_t table26_0_inner_[31]; + static const uint8_t table26_0_outer_[32]; + static const uint8_t table28_0_emit_[3]; + static const uint8_t table28_0_inner_[4]; + static const uint8_t table30_0_emit_[7]; + static const uint8_t table30_0_inner_[8]; + static const uint8_t table29_0_emit_[9]; + static const uint8_t table29_0_inner_[9]; + static const uint8_t table29_0_outer_[16]; +}; +template +class HuffDecoder : public HuffDecoderCommon { + public: + HuffDecoder(F sink, const uint8_t* begin, const uint8_t* end) + : sink_(sink), begin_(begin), end_(end) {} + bool Run() { + while (!done_) { + if (!RefillTo8()) { + Done0(); + break; + } + const auto index = (buffer_ >> (buffer_len_ - 8)) & 0xff; + const auto op = GetOp1(index); + const int consumed = op & 15; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 6; + switch ((op >> 4) & 3) { + case 1: { + DecodeStep0(); + break; + } + case 2: { + DecodeStep1(); + break; + } + case 0: { + sink_(GetEmit1(index, emit_ofs + 0)); + break; + } + } + } + return ok_; + } + + private: + bool RefillTo8() { + switch (buffer_len_) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: { + return Read1(); + } + } + return true; + } + bool Read1() { + if (end_ - begin_ < 1) return false; + buffer_ <<= 8; + buffer_ |= static_cast(*begin_++) << 0; + buffer_len_ += 8; + return true; + } + void Done0() { + done_ = true; + switch (buffer_len_) { + case 7: { + const auto index = buffer_ & 127; + const auto op = GetOp4(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit4(index, op >> 2)); + break; + } + } + return; + } + case 5: { + const auto index = buffer_ & 31; + const auto op = GetOp2(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit2(index, op >> 2)); + break; + } + } + return; + } + case 6: { + const auto index = buffer_ & 63; + const auto op = GetOp3(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit3(index, op >> 2)); + break; + } + } + return; + } + case 1: + case 2: + case 3: + case 4: { + ok_ = (buffer_ & ((1 << buffer_len_) - 1)) == (1 << buffer_len_) - 1; + return; + } + case 0: { + return; + } + } + } + void DecodeStep0() { + if (!RefillTo2()) { + Done1(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 2)) & 0x3; + const auto op = GetOp5(index); + const int consumed = op & 3; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 2; + sink_(GetEmit5(index, emit_ofs + 0)); + } + bool RefillTo2() { + switch (buffer_len_) { + case 0: + case 1: { + return Read1(); + } + } + return true; + } + void Done1() { + done_ = true; + switch (buffer_len_) { + case 0: + case 1: { + ok_ = false; + return; + } + } + } + void DecodeStep1() { + if (!RefillTo7()) { + Done2(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 7)) & 0x7f; + const auto op = GetOp6(index); + const int consumed = op & 7; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 4; + switch ((op >> 3) & 1) { + case 1: { + DecodeStep2(); + break; + } + case 0: { + sink_(GetEmit6(index, emit_ofs + 0)); + break; + } + } + } + bool RefillTo7() { + switch (buffer_len_) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: { + return Read1(); + } + } + return true; + } + void Done2() { + done_ = true; + switch (buffer_len_) { + case 4: { + const auto index = buffer_ & 15; + const auto op = GetOp9(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit9(index, op >> 2)); + break; + } + } + return; + } + case 5: { + const auto index = buffer_ & 31; + const auto op = GetOp10(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit10(index, op >> 2)); + break; + } + } + return; + } + case 2: { + const auto index = buffer_ & 3; + const auto op = GetOp7(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit7(index, op >> 2)); + break; + } + } + return; + } + case 6: { + const auto index = buffer_ & 63; + const auto op = GetOp11(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit11(index, op >> 2)); + break; + } + } + return; + } + case 3: { + const auto index = buffer_ & 7; + const auto op = GetOp8(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit8(index, op >> 2)); + break; + } + } + return; + } + case 1: { + ok_ = (buffer_ & ((1 << buffer_len_) - 1)) == (1 << buffer_len_) - 1; + return; + } + case 0: { + return; + } + } + } + void DecodeStep2() { + if (!RefillTo8()) { + Done3(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 8)) & 0xff; + const auto op = GetOp12(index); + const int consumed = op & 15; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 8; + switch ((op >> 4) & 15) { + case 8: { + DecodeStep10(); + break; + } + case 10: { + DecodeStep11(); + break; + } + case 11: { + DecodeStep12(); + break; + } + case 9: { + DecodeStep14(); + break; + } + case 1: { + DecodeStep3(); + break; + } + case 2: { + DecodeStep4(); + break; + } + case 3: { + DecodeStep5(); + break; + } + case 4: { + DecodeStep6(); + break; + } + case 5: { + DecodeStep7(); + break; + } + case 6: { + DecodeStep8(); + break; + } + case 7: { + DecodeStep9(); + break; + } + case 0: { + sink_(GetEmit12(index, emit_ofs + 0)); + break; + } + } + } + void Done3() { + done_ = true; + switch (buffer_len_) { + case 7: { + const auto index = buffer_ & 127; + const auto op = GetOp16(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit16(index, op >> 2)); + break; + } + } + return; + } + case 4: { + const auto index = buffer_ & 15; + const auto op = GetOp13(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit13(index, op >> 2)); + break; + } + } + return; + } + case 5: { + const auto index = buffer_ & 31; + const auto op = GetOp14(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit14(index, op >> 2)); + break; + } + } + return; + } + case 6: { + const auto index = buffer_ & 63; + const auto op = GetOp15(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit15(index, op >> 2)); + break; + } + } + return; + } + case 1: + case 2: + case 3: { + ok_ = (buffer_ & ((1 << buffer_len_) - 1)) == (1 << buffer_len_) - 1; + return; + } + case 0: { + return; + } + } + } + void DecodeStep3() { + if (!RefillTo1()) { + Done4(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 1)) & 0x1; + const auto op = GetOp17(index); + const int consumed = op & 1; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 1; + sink_(GetEmit17(index, emit_ofs + 0)); + } + bool RefillTo1() { + switch (buffer_len_) { + case 0: { + return Read1(); + } + } + return true; + } + void Done4() { + done_ = true; + ok_ = false; + } + void DecodeStep4() { + if (!RefillTo1()) { + Done5(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 1)) & 0x1; + const auto op = GetOp18(index); + const int consumed = op & 1; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 1; + sink_(GetEmit18(index, emit_ofs + 0)); + } + void Done5() { + done_ = true; + ok_ = false; + } + void DecodeStep5() { + if (!RefillTo1()) { + Done6(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 1)) & 0x1; + const auto op = GetOp19(index); + const int consumed = op & 1; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 1; + sink_(GetEmit19(index, emit_ofs + 0)); + } + void Done6() { + done_ = true; + ok_ = false; + } + void DecodeStep6() { + if (!RefillTo1()) { + Done7(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 1)) & 0x1; + const auto op = GetOp20(index); + const int consumed = op & 1; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 1; + sink_(GetEmit20(index, emit_ofs + 0)); + } + void Done7() { + done_ = true; + ok_ = false; + } + void DecodeStep7() { + if (!RefillTo1()) { + Done8(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 1)) & 0x1; + const auto op = GetOp21(index); + const int consumed = op & 1; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 1; + sink_(GetEmit21(index, emit_ofs + 0)); + } + void Done8() { + done_ = true; + ok_ = false; + } + void DecodeStep8() { + if (!RefillTo1()) { + Done9(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 1)) & 0x1; + const auto op = GetOp22(index); + const int consumed = op & 1; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 1; + sink_(GetEmit22(index, emit_ofs + 0)); + } + void Done9() { + done_ = true; + ok_ = false; + } + void DecodeStep9() { + if (!RefillTo2()) { + Done10(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 2)) & 0x3; + const auto op = GetOp23(index); + const int consumed = op & 3; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 2; + sink_(GetEmit23(index, emit_ofs + 0)); + } + void Done10() { + done_ = true; + switch (buffer_len_) { + case 0: + case 1: { + ok_ = false; + return; + } + } + } + void DecodeStep10() { + if (!RefillTo3()) { + Done11(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 3)) & 0x7; + const auto op = GetOp24(index); + const int consumed = op & 3; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 2; + sink_(GetEmit24(index, emit_ofs + 0)); + } + bool RefillTo3() { + switch (buffer_len_) { + case 0: + case 1: + case 2: { + return Read1(); + } + } + return true; + } + void Done11() { + done_ = true; + switch (buffer_len_) { + case 0: + case 1: + case 2: { + ok_ = false; + return; + } + } + } + void DecodeStep11() { + if (!RefillTo4()) { + Done12(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 4)) & 0xf; + const auto op = GetOp25(index); + const int consumed = op & 7; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 3; + sink_(GetEmit25(index, emit_ofs + 0)); + } + bool RefillTo4() { + switch (buffer_len_) { + case 0: + case 1: + case 2: + case 3: { + return Read1(); + } + } + return true; + } + void Done12() { + done_ = true; + switch (buffer_len_) { + case 0: + case 1: + case 2: + case 3: { + ok_ = false; + return; + } + } + } + void DecodeStep12() { + if (!RefillTo5()) { + Done13(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 5)) & 0x1f; + const auto op = GetOp26(index); + const int consumed = op & 7; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 4; + switch ((op >> 3) & 1) { + case 1: { + DecodeStep13(); + break; + } + case 0: { + sink_(GetEmit26(index, emit_ofs + 0)); + break; + } + } + } + bool RefillTo5() { + switch (buffer_len_) { + case 0: + case 1: + case 2: + case 3: + case 4: { + return Read1(); + } + } + return true; + } + void Done13() { + done_ = true; + switch (buffer_len_) { + case 4: { + const auto index = buffer_ & 15; + const auto op = GetOp27(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit27(index, op >> 2)); + break; + } + } + return; + } + case 1: + case 2: + case 3: { + ok_ = (buffer_ & ((1 << buffer_len_) - 1)) == (1 << buffer_len_) - 1; + return; + } + case 0: { + return; + } + } + } + void DecodeStep13() { + if (!RefillTo2()) { + Done14(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 2)) & 0x3; + const auto op = GetOp28(index); + const int consumed = op & 3; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 3; + switch ((op >> 2) & 1) { + case 1: { + begin_ = end_; + buffer_len_ = 0; + break; + } + case 0: { + sink_(GetEmit28(index, emit_ofs + 0)); + break; + } + } + } + void Done14() { + done_ = true; + switch (buffer_len_) { + case 1: { + ok_ = (buffer_ & ((1 << buffer_len_) - 1)) == (1 << buffer_len_) - 1; + return; + } + case 0: { + return; + } + } + } + void DecodeStep14() { + if (!RefillTo4()) { + Done15(); + return; + } + const auto index = (buffer_ >> (buffer_len_ - 4)) & 0xf; + const auto op = GetOp29(index); + const int consumed = op & 7; + buffer_len_ -= consumed; + const auto emit_ofs = op >> 3; + sink_(GetEmit29(index, emit_ofs + 0)); + } + void Done15() { + done_ = true; + switch (buffer_len_) { + case 3: { + const auto index = buffer_ & 7; + const auto op = GetOp30(index); + switch (op & 3) { + case 1: { + ok_ = false; + break; + } + case 2: { + sink_(GetEmit30(index, op >> 2)); + break; + } + } + return; + } + case 0: + case 1: + case 2: { + ok_ = false; + return; + } + } + } + F sink_; + const uint8_t* begin_; + const uint8_t* const end_; + uint64_t buffer_ = 0; + int buffer_len_ = 0; + bool ok_ = true; + bool done_ = false; +}; +} // namespace grpc_core +#endif // GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_DECODE_HUFF_H diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc index cfb46a1be3a..f2e49022dc3 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc @@ -43,10 +43,12 @@ #include #include +#include "src/core/ext/transport/chttp2/transport/decode_huff.h" #include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" #include "src/core/ext/transport/chttp2/transport/hpack_constants.h" #include "src/core/ext/transport/chttp2/transport/internal.h" #include "src/core/lib/debug/trace.h" +#include "src/core/lib/experiments/experiments.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/slice/slice.h" @@ -64,8 +66,9 @@ TraceFlag grpc_trace_chttp2_hpack_parser(false, "chttp2_hpack_parser"); /* state table for huffman decoding: given a state, gives an index/16 into next_sub_tbl. Taking that index and adding the value of the nibble being considered returns the next state. - - generated by gen_hpack_tables.c */ + generated by gen_hpack_tables.c + TODO(ctiller): remove once the new_hpack_huffman_decoder experiment is + complete. */ static const uint8_t next_tbl[256] = { 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8, 1, 3, 3, 9, 10, 11, 1, 1, 1, 12, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, @@ -141,7 +144,6 @@ static const int16_t next_sub_tbl[48 * 16] = { /* emission table: indexed like next_tbl, ultimately gives the byte to be emitted, or -1 for no byte, or 256 for end of stream - generated by gen_hpack_tables.c */ static const uint16_t emit_tbl[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, @@ -764,21 +766,6 @@ class HPackParser::String { // decoded byte. template static bool ParseHuff(Input* input, uint32_t length, Out output) { - int16_t state = 0; - // Parse one half byte... we leverage some lookup tables to keep the logic - // here really simple. - auto nibble = [&output, &state](uint8_t nibble) { - int16_t emit = emit_sub_tbl[16 * emit_tbl[state] + nibble]; - int16_t next = next_sub_tbl[16 * next_tbl[state] + nibble]; - if (emit != -1) { - if (emit >= 0 && emit < 256) { - output(static_cast(emit)); - } else { - assert(emit == 256); - } - } - state = next; - }; // If there's insufficient bytes remaining, return now. if (input->remaining() < length) { return input->UnexpectedEOF(false); @@ -786,11 +773,30 @@ class HPackParser::String { // Grab the byte range, and iterate through it. const uint8_t* p = input->cur_ptr(); input->Advance(length); - for (uint32_t i = 0; i < length; i++) { - nibble(p[i] >> 4); - nibble(p[i] & 0xf); + if (IsNewHpackHuffmanDecoderEnabled()) { + return HuffDecoder(output, p, p + length).Run(); + } else { + int16_t state = 0; + // Parse one half byte... we leverage some lookup tables to keep the logic + // here really simple. + auto nibble = [&output, &state](uint8_t nibble) { + int16_t emit = emit_sub_tbl[16 * emit_tbl[state] + nibble]; + int16_t next = next_sub_tbl[16 * next_tbl[state] + nibble]; + if (emit != -1) { + if (emit >= 0 && emit < 256) { + output(static_cast(emit)); + } else { + assert(emit == 256); + } + } + state = next; + }; + for (uint32_t i = 0; i < length; i++) { + nibble(p[i] >> 4); + nibble(p[i] & 0xf); + } + return true; } - return true; } // Parse some uncompressed string bytes. diff --git a/src/core/lib/experiments/experiments.cc b/src/core/lib/experiments/experiments.cc index 683b4880d1e..4a59747e541 100644 --- a/src/core/lib/experiments/experiments.cc +++ b/src/core/lib/experiments/experiments.cc @@ -41,6 +41,9 @@ const char* const description_periodic_resource_quota_reclamation = "Periodically return memory to the resource quota"; const char* const description_unconstrained_max_quota_buffer_size = "Discard the cap on the max free pool size for one memory allocator"; +const char* const description_new_hpack_huffman_decoder = + "New HPACK huffman decoder - should be much faster than the existing " + "implementation."; } // namespace namespace grpc_core { @@ -57,6 +60,7 @@ const ExperimentMetadata g_experiment_metadata[] = { description_periodic_resource_quota_reclamation, false}, {"unconstrained_max_quota_buffer_size", description_unconstrained_max_quota_buffer_size, false}, + {"new_hpack_huffman_decoder", description_new_hpack_huffman_decoder, false}, }; } // namespace grpc_core diff --git a/src/core/lib/experiments/experiments.h b/src/core/lib/experiments/experiments.h index 7d812c0fa4b..f70ab04e55b 100644 --- a/src/core/lib/experiments/experiments.h +++ b/src/core/lib/experiments/experiments.h @@ -39,6 +39,7 @@ inline bool IsPeriodicResourceQuotaReclamationEnabled() { inline bool IsUnconstrainedMaxQuotaBufferSizeEnabled() { return IsExperimentEnabled(7); } +inline bool IsNewHpackHuffmanDecoderEnabled() { return IsExperimentEnabled(8); } struct ExperimentMetadata { const char* name; @@ -46,7 +47,7 @@ struct ExperimentMetadata { bool default_value; }; -constexpr const size_t kNumExperiments = 8; +constexpr const size_t kNumExperiments = 9; extern const ExperimentMetadata g_experiment_metadata[kNumExperiments]; } // namespace grpc_core diff --git a/src/core/lib/experiments/experiments.yaml b/src/core/lib/experiments/experiments.yaml index 36195330f43..3923d8d5b84 100644 --- a/src/core/lib/experiments/experiments.yaml +++ b/src/core/lib/experiments/experiments.yaml @@ -31,6 +31,7 @@ # core_end2end_tests: all tests, fixtures in the core end2end suite # endpoint_test: endpoint related iomgr tests # flow_control_test: tests pertaining explicitly to flow control +# hpack_test: hpack encode/decode tests - name: tcp_frame_size_tuning description: @@ -94,3 +95,10 @@ expiry: 2022/10/01 owner: ctiller@google.com test_tags: [resource_quota_test] +- name: new_hpack_huffman_decoder + description: + New HPACK huffman decoder - should be much faster than the existing implementation. + default: false + expiry: 2022/10/01 + owner: ctiller@google.com + test_tags: ["core_end2end_tests", "hpack_test"] diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 81afd318fce..a41ede220f2 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -93,6 +93,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', 'src/core/ext/transport/chttp2/transport/context_list.cc', + 'src/core/ext/transport/chttp2/transport/decode_huff.cc', 'src/core/ext/transport/chttp2/transport/flow_control.cc', 'src/core/ext/transport/chttp2/transport/frame_data.cc', 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', diff --git a/test/core/transport/chttp2/BUILD b/test/core/transport/chttp2/BUILD index 97f8ccdc3a5..0ddcee81dd6 100644 --- a/test/core/transport/chttp2/BUILD +++ b/test/core/transport/chttp2/BUILD @@ -13,7 +13,7 @@ # limitations under the License. load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package") -load("//test/core/util:grpc_fuzzer.bzl", "grpc_proto_fuzzer") +load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer", "grpc_proto_fuzzer") load("//bazel:custom_exec_properties.bzl", "LARGE_MACHINE") licenses(["notice"]) @@ -47,6 +47,39 @@ grpc_proto_fuzzer( ], ) +grpc_fuzzer( + name = "decode_huff_fuzzer", + srcs = ["decode_huff_fuzzer.cc"], + corpus = "decode_huff_corpus", + external_deps = [ + "absl/types:optional", + "absl/strings", + ], + language = "C++", + tags = ["no_windows"], + deps = [ + "//:decode_huff", + "//:huffsyms", + ], +) + +grpc_fuzzer( + name = "encode_decode_huff_fuzzer", + srcs = ["encode_decode_huff_fuzzer.cc"], + corpus = "encode_decode_huff_corpus", + external_deps = [ + "absl/types:optional", + "absl/strings", + ], + language = "C++", + tags = ["no_windows"], + deps = [ + "//:decode_huff", + "//:grpc", + "//:huffsyms", + ], +) + grpc_cc_test( name = "alpn_test", srcs = ["alpn_test.cc"], @@ -138,6 +171,7 @@ grpc_cc_test( srcs = ["hpack_encoder_test.cc"], external_deps = ["gtest"], language = "C++", + tags = ["hpack_test"], uses_event_engine = False, uses_polling = False, deps = [ @@ -153,6 +187,7 @@ grpc_cc_test( srcs = ["hpack_parser_test.cc"], external_deps = ["gtest"], language = "C++", + tags = ["hpack_test"], uses_event_engine = False, uses_polling = False, deps = [ @@ -168,6 +203,7 @@ grpc_cc_test( srcs = ["hpack_parser_table_test.cc"], external_deps = ["gtest"], language = "C++", + tags = ["hpack_test"], uses_event_engine = False, uses_polling = False, deps = [ diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-00a9c09e40a11d1c19e21f12d60b59648625d8fd b/test/core/transport/chttp2/decode_huff_corpus/crash-00a9c09e40a11d1c19e21f12d60b59648625d8fd new file mode 100644 index 00000000000..2e7d2f0b106 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-00a9c09e40a11d1c19e21f12d60b59648625d8fd @@ -0,0 +1 @@ +g0 diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-0d8fd3973daf59cc1ee5f7974a71412617b71440 b/test/core/transport/chttp2/decode_huff_corpus/crash-0d8fd3973daf59cc1ee5f7974a71412617b71440 new file mode 100644 index 00000000000..71b279ffa59 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-0d8fd3973daf59cc1ee5f7974a71412617b71440 @@ -0,0 +1 @@ +¯¯û \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-301a842e41331e623aa5b84648bdc1b60ccef604 b/test/core/transport/chttp2/decode_huff_corpus/crash-301a842e41331e623aa5b84648bdc1b60ccef604 new file mode 100644 index 00000000000..d3d10efde05 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-301a842e41331e623aa5b84648bdc1b60ccef604 @@ -0,0 +1 @@ +!÷ÿˆ \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-3313769f3f71f18d9c30cab50913e5ac8c7f2a7c b/test/core/transport/chttp2/decode_huff_corpus/crash-3313769f3f71f18d9c30cab50913e5ac8c7f2a7c new file mode 100644 index 00000000000..ba2e9dcdaa0 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-3313769f3f71f18d9c30cab50913e5ac8c7f2a7c @@ -0,0 +1 @@ +§ÿÿê \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-66d5f3db270089b431d1e7ecf3e5441663bc3c68 b/test/core/transport/chttp2/decode_huff_corpus/crash-66d5f3db270089b431d1e7ecf3e5441663bc3c68 new file mode 100644 index 00000000000..2387b107704 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-66d5f3db270089b431d1e7ecf3e5441663bc3c68 @@ -0,0 +1 @@ +Ýý= \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-7ee80075e2b622257287fa44ba804acbb77f319a b/test/core/transport/chttp2/decode_huff_corpus/crash-7ee80075e2b622257287fa44ba804acbb77f319a new file mode 100644 index 00000000000..65066462d56 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-7ee80075e2b622257287fa44ba804acbb77f319a @@ -0,0 +1 @@ +Ýý \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-82795fa372e1a5a3ed35e318b82a591cfe3df24e b/test/core/transport/chttp2/decode_huff_corpus/crash-82795fa372e1a5a3ed35e318b82a591cfe3df24e new file mode 100644 index 00000000000..880b95841bc --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-82795fa372e1a5a3ed35e318b82a591cfe3df24e @@ -0,0 +1 @@ +t~ \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-a19f987b885f5a96069f4bc7f12b9e84ceba7dfa b/test/core/transport/chttp2/decode_huff_corpus/crash-a19f987b885f5a96069f4bc7f12b9e84ceba7dfa new file mode 100644 index 00000000000..f96c401f328 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-a19f987b885f5a96069f4bc7f12b9e84ceba7dfa @@ -0,0 +1 @@ +ÿÿ \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-adc83b19e793491b1c6ea0fd8b46cd9f32e592fc b/test/core/transport/chttp2/decode_huff_corpus/crash-adc83b19e793491b1c6ea0fd8b46cd9f32e592fc new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-adc83b19e793491b1c6ea0fd8b46cd9f32e592fc @@ -0,0 +1 @@ + diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709 b/test/core/transport/chttp2/decode_huff_corpus/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-efa098c27cd3874d3a4fe9c1dc2551a5a392e45f b/test/core/transport/chttp2/decode_huff_corpus/crash-efa098c27cd3874d3a4fe9c1dc2551a5a392e45f new file mode 100644 index 00000000000..ab6fb48f330 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-efa098c27cd3874d3a4fe9c1dc2551a5a392e45f @@ -0,0 +1 @@ +šÿÿÿ \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/crash-fa6878f79c7c4ec40c1362352c181bd30f132824 b/test/core/transport/chttp2/decode_huff_corpus/crash-fa6878f79c7c4ec40c1362352c181bd30f132824 new file mode 100644 index 00000000000..630de5ca0b3 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_corpus/crash-fa6878f79c7c4ec40c1362352c181bd30f132824 @@ -0,0 +1 @@ +ïïïKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK_ÿ_ÿïïïïÿÿï \ No newline at end of file diff --git a/test/core/transport/chttp2/decode_huff_corpus/empty b/test/core/transport/chttp2/decode_huff_corpus/empty new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/core/transport/chttp2/decode_huff_fuzzer.cc b/test/core/transport/chttp2/decode_huff_fuzzer.cc new file mode 100644 index 00000000000..93009babae4 --- /dev/null +++ b/test/core/transport/chttp2/decode_huff_fuzzer.cc @@ -0,0 +1,100 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/types/optional.h" + +#include "src/core/ext/transport/chttp2/transport/decode_huff.h" +#include "src/core/ext/transport/chttp2/transport/huffsyms.h" + +bool squelch = true; +bool leak_check = true; + +absl::optional> DecodeHuffSlow(const uint8_t* begin, + const uint8_t* end) { + uint64_t bits = 0; + int bits_left = 0; + std::vector out; + while (true) { + while (begin != end && bits_left < 30) { + bits <<= 8; + bits |= begin[0]; + ++begin; + bits_left += 8; + } + if (bits_left < 5) break; + bool found = false; + for (int i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) { + const auto& sym = grpc_chttp2_huffsyms[i]; + if (sym.length > bits_left) continue; + if (((bits >> (bits_left - sym.length)) & ((1 << sym.length) - 1)) == + sym.bits) { + found = true; + bits_left -= sym.length; + if (i == 256) { + return out; + } + out.push_back(i); + break; + } + } + if (!found) { + break; + } + } + while (bits_left > 0) { + if ((bits & 1) == 0) { + return absl::nullopt; + } + bits >>= 1; + bits_left--; + } + return out; +} + +std::string ToString(absl::optional> s) { + if (s == absl::nullopt) return "nullopt"; + return absl::StrCat("{", absl::StrJoin(*s, ","), "}"); +} + +absl::optional> DecodeHuffFast(const uint8_t* begin, + const uint8_t* end) { + std::vector v; + auto f = [&](uint8_t x) { v.push_back(x); }; + if (!grpc_core::HuffDecoder(f, begin, end).Run()) { + return absl::nullopt; + } + return v; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + auto slow = DecodeHuffSlow(data, data + size); + auto fast = DecodeHuffFast(data, data + size); + if (slow != fast) { + fprintf(stderr, "MISMATCH:\ninpt: %s\nslow: %s\nfast: %s\n", + ToString(std::vector(data, data + size)).c_str(), + ToString(slow).c_str(), ToString(fast).c_str()); + abort(); + } + return 0; +} diff --git a/test/core/transport/chttp2/encode_decode_huff_corpus/crash-71853c6197a6a7f222db0f1978c7cb232b87c5ee b/test/core/transport/chttp2/encode_decode_huff_corpus/crash-71853c6197a6a7f222db0f1978c7cb232b87c5ee new file mode 100644 index 00000000000..139597f9cb0 --- /dev/null +++ b/test/core/transport/chttp2/encode_decode_huff_corpus/crash-71853c6197a6a7f222db0f1978c7cb232b87c5ee @@ -0,0 +1,2 @@ + + diff --git a/test/core/transport/chttp2/encode_decode_huff_corpus/crash-a7f81c6ab53512fdae09057902ff3867a3cc8b10 b/test/core/transport/chttp2/encode_decode_huff_corpus/crash-a7f81c6ab53512fdae09057902ff3867a3cc8b10 new file mode 100644 index 00000000000..083367785bb --- /dev/null +++ b/test/core/transport/chttp2/encode_decode_huff_corpus/crash-a7f81c6ab53512fdae09057902ff3867a3cc8b10 @@ -0,0 +1 @@ +W \ No newline at end of file diff --git a/test/core/transport/chttp2/encode_decode_huff_fuzzer.cc b/test/core/transport/chttp2/encode_decode_huff_fuzzer.cc new file mode 100644 index 00000000000..5d1f9c46718 --- /dev/null +++ b/test/core/transport/chttp2/encode_decode_huff_fuzzer.cc @@ -0,0 +1,76 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/types/optional.h" + +#include + +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/ext/transport/chttp2/transport/decode_huff.h" + +bool squelch = true; +bool leak_check = true; + +std::string ToString(absl::optional> s) { + if (s == absl::nullopt) return "nullopt"; + return absl::StrCat("{", absl::StrJoin(*s, ","), "}"); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + grpc_slice uncompressed = + grpc_slice_from_copied_buffer(reinterpret_cast(data), size); + grpc_slice compressed = grpc_chttp2_huffman_compress(uncompressed); + std::vector uncompressed_again; + auto add = [&uncompressed_again](uint8_t c) { + uncompressed_again.push_back(c); + }; + auto fail = [&](const char* reason) { + fprintf(stderr, + "Failed: %s\nuncompressed: %s\ncompressed: %s\nuncompressed_again: " + "%s\n", + reason, ToString(std::vector(data, data + size)).c_str(), + ToString(std::vector(GRPC_SLICE_START_PTR(compressed), + GRPC_SLICE_START_PTR(compressed) + + GRPC_SLICE_LENGTH(compressed))) + .c_str(), + ToString(uncompressed_again).c_str()); + abort(); + }; + if (!grpc_core::HuffDecoder(add, + GRPC_SLICE_START_PTR(compressed), + GRPC_SLICE_END_PTR(compressed)) + .Run()) { + fail("decoding"); + } + if (uncompressed_again.size() != size) { + fail("size mismatch"); + } + if (memcmp(uncompressed_again.data(), data, size) != 0) { + fail("data mismatch"); + } + grpc_slice_unref(uncompressed); + grpc_slice_unref(compressed); + return 0; +} diff --git a/test/cpp/microbenchmarks/BUILD b/test/cpp/microbenchmarks/BUILD index bc280ffb047..eb53a8877d8 100644 --- a/test/cpp/microbenchmarks/BUILD +++ b/test/cpp/microbenchmarks/BUILD @@ -96,6 +96,17 @@ grpc_cc_test( deps = [":helpers"], ) +grpc_cc_test( + name = "bm_huffman_decode", + srcs = ["bm_huffman_decode.cc"], + args = grpc_benchmark_args(), + tags = [ + "no_mac", + "no_windows", + ], + deps = [":helpers"], +) + grpc_cc_test( name = "bm_alarm", srcs = ["bm_alarm.cc"], diff --git a/test/cpp/microbenchmarks/bm_huffman_decode.cc b/test/cpp/microbenchmarks/bm_huffman_decode.cc new file mode 100644 index 00000000000..75f21409410 --- /dev/null +++ b/test/cpp/microbenchmarks/bm_huffman_decode.cc @@ -0,0 +1,468 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include + +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/ext/transport/chttp2/transport/decode_huff.h" +#include "src/core/lib/slice/slice.h" +#include "test/core/util/test_config.h" + +static const std::vector* kInput = [] { + std::vector v; + std::mt19937 rd(0); + std::uniform_int_distribution<> dist_ty(0, 100); + std::uniform_int_distribution<> dist_byte(0, 255); + std::uniform_int_distribution<> dist_normal(32, 126); + for (int i = 0; i < 1024 * 1024; i++) { + if (dist_ty(rd) == 1) { + v.push_back(dist_byte(rd)); + } else { + v.push_back(dist_normal(rd)); + } + } + grpc_core::Slice s = grpc_core::Slice::FromCopiedBuffer(v); + grpc_core::Slice c(grpc_chttp2_huffman_compress(s.c_slice())); + return new std::vector(c.begin(), c.end()); +}(); + +static void BM_Decode(benchmark::State& state) { + std::vector output; + auto add = [&output](uint8_t c) { output.push_back(c); }; + for (auto _ : state) { + output.clear(); + grpc_core::HuffDecoder(add, kInput->data(), + kInput->data() + kInput->size()) + .Run(); + } +} +BENCHMARK(BM_Decode); + +// Legacy huffman decoder +static void BM_LegacyDecode(benchmark::State& state) { + /* state table for huffman decoding: given a state, gives an index/16 into + next_sub_tbl. Taking that index and adding the value of the nibble being + considered returns the next state. + generated by gen_hpack_tables.c */ + static const uint8_t next_tbl[256] = { + 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8, 1, 3, 3, 9, 10, 11, + 1, 1, 1, 12, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 14, 1, 15, 16, 1, 17, 1, 15, 2, 7, 3, 18, 19, 1, + 1, 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 7, + 21, 1, 22, 1, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, + 23, 24, 25, 1, 1, 1, 1, 2, 2, 2, 26, 3, 3, 27, 10, 28, 1, 1, + 1, 1, 1, 1, 2, 3, 29, 10, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 32, 1, 1, 15, 33, 1, 34, 35, 9, 36, 1, 1, 1, 1, 1, + 1, 1, 37, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 26, 9, + 38, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 2, 2, 26, 3, 3, 39, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 7, 3, + 3, 3, 40, 2, 41, 1, 1, 1, 42, 43, 1, 1, 44, 1, 1, 1, 1, 15, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 45, 46, 1, 1, 2, 2, 2, 35, 3, + 3, 18, 47, 2, + }; + + /* next state, based upon current state and the current nibble: see above. + generated by gen_hpack_tables.c */ + static const int16_t next_sub_tbl[48 * 16] = { + 1, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 2, 6, 10, 13, 14, 15, 16, 17, 2, 6, 10, 13, 14, 15, + 16, 17, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, 24, 3, + 7, 11, 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 199, 200, 201, 202, 203, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 3, 7, 11, 24, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 132, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 21, 4, 8, 4, + 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 22, 23, 91, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 3, + 7, 11, 24, 3, 7, 11, 24, 0, 0, 0, 0, 0, 41, 42, 43, + 2, 6, 10, 13, 14, 15, 16, 17, 3, 7, 11, 24, 3, 7, 11, + 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 44, 45, 2, 6, 10, 13, 14, 15, 16, 17, 46, 47, 48, 49, 50, + 51, 52, 57, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 73, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 3, 7, 11, 24, 3, 7, 11, + 24, 3, 7, 11, 24, 0, 0, 0, 0, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 92, 0, 0, 0, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, + 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 4, + 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 2, 6, 10, 13, 14, 15, 16, 17, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 148, + 149, 150, 151, 3, 7, 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, + 0, 0, 152, 153, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, + 24, 154, 155, 156, 164, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 197, 198, 4, 8, 4, 8, 4, + 8, 4, 8, 0, 0, 0, 0, 0, 0, 219, 220, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 0, 0, 221, 222, 223, 224, 3, 7, 11, + 24, 3, 7, 11, 24, 4, 8, 4, 8, 4, 8, 225, 228, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, + }; + + /* emission table: indexed like next_tbl, ultimately gives the byte to be + emitted, or -1 for no byte, or 256 for end of stream + generated by gen_hpack_tables.c */ + static const uint16_t emit_tbl[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 0, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 0, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 0, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 0, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, + }; + + /* generated by gen_hpack_tables.c */ + static const int16_t emit_sub_tbl[249 * 16] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, + 49, 49, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 97, + 97, 97, 97, 48, 48, 49, 49, 50, 50, 97, 97, 99, 99, 101, 101, + 105, 105, 111, 111, 48, 49, 50, 97, 99, 101, 105, 111, 115, 116, -1, + -1, -1, -1, -1, -1, 32, 32, 32, 32, 32, 32, 32, 32, 37, 37, + 37, 37, 37, 37, 37, 37, 99, 99, 99, 99, 101, 101, 101, 101, 105, + 105, 105, 105, 111, 111, 111, 111, 115, 115, 116, 116, 32, 37, 45, 46, + 47, 51, 52, 53, 54, 55, 56, 57, 61, 61, 61, 61, 61, 61, 61, + 61, 65, 65, 65, 65, 65, 65, 65, 65, 115, 115, 115, 115, 116, 116, + 116, 116, 32, 32, 37, 37, 45, 45, 46, 46, 61, 65, 95, 98, 100, + 102, 103, 104, 108, 109, 110, 112, 114, 117, -1, -1, 58, 58, 58, 58, + 58, 58, 58, 58, 66, 66, 66, 66, 66, 66, 66, 66, 47, 47, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 61, 61, + 65, 65, 95, 95, 98, 98, 100, 100, 102, 102, 103, 103, 104, 104, 108, + 108, 109, 109, 110, 110, 112, 112, 114, 114, 117, 117, 58, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 89, 106, 107, 113, 118, 119, 120, 121, 122, -1, -1, + -1, -1, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 42, 42, 42, + 42, 42, 42, 44, 44, 44, 44, 44, 44, 44, 44, 59, 59, 59, 59, + 59, 59, 59, 59, 88, 88, 88, 88, 88, 88, 88, 88, 90, 90, 90, + 90, 90, 90, 90, 90, 33, 33, 34, 34, 40, 40, 41, 41, 63, 63, + 39, 43, 124, -1, -1, -1, 35, 35, 35, 35, 35, 35, 35, 35, 62, + 62, 62, 62, 62, 62, 62, 62, 0, 0, 0, 0, 36, 36, 36, 36, + 64, 64, 64, 64, 91, 91, 91, 91, 69, 69, 69, 69, 69, 69, 69, + 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, + 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, + 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, + 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, + 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, + 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 81, + 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, + 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, + 84, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, + 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 89, 89, 89, 89, 89, + 89, 89, 89, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, + 107, 107, 107, 107, 113, 113, 113, 113, 113, 113, 113, 113, 118, 118, 118, + 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, + 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122, + 122, 122, 122, 122, 122, 122, 122, 38, 38, 38, 38, 42, 42, 42, 42, + 44, 44, 44, 44, 59, 59, 59, 59, 88, 88, 88, 88, 90, 90, 90, + 90, 33, 34, 40, 41, 63, -1, -1, -1, 39, 39, 39, 39, 39, 39, + 39, 39, 43, 43, 43, 43, 43, 43, 43, 43, 124, 124, 124, 124, 124, + 124, 124, 124, 35, 35, 35, 35, 62, 62, 62, 62, 0, 0, 36, 36, + 64, 64, 91, 91, 93, 93, 126, 126, 94, 125, -1, -1, 60, 60, 60, + 60, 60, 60, 60, 60, 96, 96, 96, 96, 96, 96, 96, 96, 123, 123, + 123, 123, 123, 123, 123, 123, -1, -1, -1, -1, -1, -1, -1, -1, 92, + 92, 92, 92, 92, 92, 92, 92, 195, 195, 195, 195, 195, 195, 195, 195, + 208, 208, 208, 208, 208, 208, 208, 208, 128, 128, 128, 128, 130, 130, 130, + 130, 131, 131, 131, 131, 162, 162, 162, 162, 184, 184, 184, 184, 194, 194, + 194, 194, 224, 224, 224, 224, 226, 226, 226, 226, 153, 153, 161, 161, 167, + 167, 172, 172, 176, 176, 177, 177, 179, 179, 209, 209, 216, 216, 217, 217, + 227, 227, 229, 229, 230, 230, 129, 132, 133, 134, 136, 146, 154, 156, 160, + 163, 164, 169, 170, 173, 178, 181, 185, 186, 187, 189, 190, 196, 198, 228, + 232, 233, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 135, + 135, 135, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 137, 137, 137, + 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, + 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, + 141, 141, 143, 143, 143, 143, 143, 143, 143, 143, 147, 147, 147, 147, 147, + 147, 147, 147, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, + 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, + 152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 155, 157, 157, + 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 165, + 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, + 168, 168, 168, 168, 168, 168, 168, 168, 174, 174, 174, 174, 174, 174, 174, + 174, 175, 175, 175, 175, 175, 175, 175, 175, 180, 180, 180, 180, 180, 180, + 180, 180, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, + 183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191, + 191, 191, 191, 191, 197, 197, 197, 197, 197, 197, 197, 197, 231, 231, 231, + 231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 9, 9, + 9, 9, 142, 142, 142, 142, 144, 144, 144, 144, 145, 145, 145, 145, 148, + 148, 148, 148, 159, 159, 159, 159, 171, 171, 171, 171, 206, 206, 206, 206, + 215, 215, 215, 215, 225, 225, 225, 225, 236, 236, 236, 236, 237, 237, 237, + 237, 199, 199, 207, 207, 234, 234, 235, 235, 192, 193, 200, 201, 202, 205, + 210, 213, 218, 219, 238, 240, 242, 243, 255, -1, 203, 203, 203, 203, 203, + 203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 204, 211, 211, 211, 211, + 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 214, 214, + 214, 214, 214, 214, 214, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222, + 222, 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 241, + 241, 241, 241, 241, 241, 241, 241, 244, 244, 244, 244, 244, 244, 244, 244, + 245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, + 246, 247, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248, + 248, 248, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, + 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, + 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, + 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 11, 12, + 12, 12, 12, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, + 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, + 20, 21, 21, 21, 21, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, + 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 127, 127, 127, 127, + 220, 220, 220, 220, 249, 249, 249, 249, 10, 13, 22, 256, 93, 93, 93, + 93, 126, 126, 126, 126, 94, 94, 125, 125, 60, 96, 123, -1, 92, 195, + 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 128, + 128, 128, 128, 128, 128, 128, 128, 130, 130, 130, 130, 130, 130, 130, 130, + 131, 131, 131, 131, 131, 131, 131, 131, 162, 162, 162, 162, 162, 162, 162, + 162, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 194, 194, 194, 194, + 194, 194, 224, 224, 224, 224, 224, 224, 224, 224, 226, 226, 226, 226, 226, + 226, 226, 226, 153, 153, 153, 153, 161, 161, 161, 161, 167, 167, 167, 167, + 172, 172, 172, 172, 176, 176, 176, 176, 177, 177, 177, 177, 179, 179, 179, + 179, 209, 209, 209, 209, 216, 216, 216, 216, 217, 217, 217, 217, 227, 227, + 227, 227, 229, 229, 229, 229, 230, 230, 230, 230, 129, 129, 132, 132, 133, + 133, 134, 134, 136, 136, 146, 146, 154, 154, 156, 156, 160, 160, 163, 163, + 164, 164, 169, 169, 170, 170, 173, 173, 178, 178, 181, 181, 185, 185, 186, + 186, 187, 187, 189, 189, 190, 190, 196, 196, 198, 198, 228, 228, 232, 232, + 233, 233, 1, 135, 137, 138, 139, 140, 141, 143, 147, 149, 150, 151, 152, + 155, 157, 158, 165, 166, 168, 174, 175, 180, 182, 183, 188, 191, 197, 231, + 239, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, 9, + 9, 9, 9, 9, 9, 142, 142, 142, 142, 142, 142, 142, 142, 144, 144, + 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 148, + 148, 148, 148, 148, 148, 148, 148, 159, 159, 159, 159, 159, 159, 159, 159, + 171, 171, 171, 171, 171, 171, 171, 171, 206, 206, 206, 206, 206, 206, 206, + 206, 215, 215, 215, 215, 215, 215, 215, 215, 225, 225, 225, 225, 225, 225, + 225, 225, 236, 236, 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237, + 237, 237, 237, 199, 199, 199, 199, 207, 207, 207, 207, 234, 234, 234, 234, + 235, 235, 235, 235, 192, 192, 193, 193, 200, 200, 201, 201, 202, 202, 205, + 205, 210, 210, 213, 213, 218, 218, 219, 219, 238, 238, 240, 240, 242, 242, + 243, 243, 255, 255, 203, 204, 211, 212, 214, 221, 222, 223, 241, 244, 245, + 246, 247, 248, 250, 251, 252, 253, 254, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, + 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, + 20, 21, 21, 21, 21, 21, 21, 21, 21, 23, 23, 23, 23, 23, 23, + 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, + 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, + 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, + 31, 31, 31, 31, 31, 31, 127, 127, 127, 127, 127, 127, 127, 127, 220, + 220, 220, 220, 220, 220, 220, 220, 249, 249, 249, 249, 249, 249, 249, 249, + 10, 10, 13, 13, 22, 22, 256, 256, 67, 67, 67, 67, 67, 67, 67, + 67, 68, 68, 68, 68, 68, 68, 68, 68, 95, 95, 95, 95, 95, 95, + 95, 95, 98, 98, 98, 98, 98, 98, 98, 98, 100, 100, 100, 100, 100, + 100, 100, 100, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, + 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 108, 108, 108, + 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, + 110, 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 112, 112, 114, + 114, 114, 114, 114, 114, 114, 114, 117, 117, 117, 117, 117, 117, 117, 117, + 58, 58, 58, 58, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, + 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, + 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, + 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, + 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, + 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, + 87, 87, 89, 89, 89, 89, 106, 106, 106, 106, 107, 107, 107, 107, 113, + 113, 113, 113, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120, + 121, 121, 121, 121, 122, 122, 122, 122, 38, 38, 42, 42, 44, 44, 59, + 59, 88, 88, 90, 90, -1, -1, -1, -1, 33, 33, 33, 33, 33, 33, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, + 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 63, 63, 63, 63, + 63, 63, 63, 63, 39, 39, 39, 39, 43, 43, 43, 43, 124, 124, 124, + 124, 35, 35, 62, 62, 0, 36, 64, 91, 93, 126, -1, -1, 94, 94, + 94, 94, 94, 94, 94, 94, 125, 125, 125, 125, 125, 125, 125, 125, 60, + 60, 60, 60, 96, 96, 96, 96, 123, 123, 123, 123, -1, -1, -1, -1, + 92, 92, 92, 92, 195, 195, 195, 195, 208, 208, 208, 208, 128, 128, 130, + 130, 131, 131, 162, 162, 184, 184, 194, 194, 224, 224, 226, 226, 153, 161, + 167, 172, 176, 177, 179, 209, 216, 217, 227, 229, 230, -1, -1, -1, -1, + -1, -1, -1, 129, 129, 129, 129, 129, 129, 129, 129, 132, 132, 132, 132, + 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, + 134, 134, 134, 134, 134, 136, 136, 136, 136, 136, 136, 136, 136, 146, 146, + 146, 146, 146, 146, 146, 146, 154, 154, 154, 154, 154, 154, 154, 154, 156, + 156, 156, 156, 156, 156, 156, 156, 160, 160, 160, 160, 160, 160, 160, 160, + 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, + 164, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, + 170, 170, 173, 173, 173, 173, 173, 173, 173, 173, 178, 178, 178, 178, 178, + 178, 178, 178, 181, 181, 181, 181, 181, 181, 181, 181, 185, 185, 185, 185, + 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, + 187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, + 190, 190, 190, 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 198, + 198, 198, 198, 198, 198, 198, 198, 228, 228, 228, 228, 228, 228, 228, 228, + 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233, + 233, 1, 1, 1, 1, 135, 135, 135, 135, 137, 137, 137, 137, 138, 138, + 138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 143, + 143, 143, 143, 147, 147, 147, 147, 149, 149, 149, 149, 150, 150, 150, 150, + 151, 151, 151, 151, 152, 152, 152, 152, 155, 155, 155, 155, 157, 157, 157, + 157, 158, 158, 158, 158, 165, 165, 165, 165, 166, 166, 166, 166, 168, 168, + 168, 168, 174, 174, 174, 174, 175, 175, 175, 175, 180, 180, 180, 180, 182, + 182, 182, 182, 183, 183, 183, 183, 188, 188, 188, 188, 191, 191, 191, 191, + 197, 197, 197, 197, 231, 231, 231, 231, 239, 239, 239, 239, 9, 9, 142, + 142, 144, 144, 145, 145, 148, 148, 159, 159, 171, 171, 206, 206, 215, 215, + 225, 225, 236, 236, 237, 237, 199, 207, 234, 235, 192, 192, 192, 192, 192, + 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 200, 200, 200, 200, + 200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202, + 202, 202, 202, 202, 202, 205, 205, 205, 205, 205, 205, 205, 205, 210, 210, + 210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 213, 218, + 218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219, + 238, 238, 238, 238, 238, 238, 238, 238, 240, 240, 240, 240, 240, 240, 240, + 240, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243, + 243, 243, 255, 255, 255, 255, 255, 255, 255, 255, 203, 203, 203, 203, 204, + 204, 204, 204, 211, 211, 211, 211, 212, 212, 212, 212, 214, 214, 214, 214, + 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 241, 241, 241, + 241, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, + 247, 247, 248, 248, 248, 248, 250, 250, 250, 250, 251, 251, 251, 251, 252, + 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 11, 11, 12, 12, 14, + 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, + 30, 31, 31, 127, 127, 220, 220, 249, 249, -1, -1, 10, 10, 10, 10, + 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 22, 22, 22, + 22, 22, 22, 22, 22, 256, 256, 256, 256, 256, 256, 256, 256, 45, 45, + 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47, + 47, 47, 47, 47, 47, 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, + 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, + 53, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, + 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, + 57, 57, 57, 50, 50, 50, 50, 50, 50, 50, 50, 97, 97, 97, 97, + 97, 97, 97, 97, 99, 99, 99, 99, 99, 99, 99, 99, 101, 101, 101, + 101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 111, 111, + 111, 111, 111, 111, 111, 111, 115, 115, 115, 115, 115, 115, 115, 115, 116, + 116, 116, 116, 116, 116, 116, 116, 32, 32, 32, 32, 37, 37, 37, 37, + 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 51, 51, 51, + 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, + 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 61, 61, 61, 61, 65, + 65, 65, 65, 95, 95, 95, 95, 98, 98, 98, 98, 100, 100, 100, 100, + 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 108, 108, 108, + 108, 109, 109, 109, 109, 110, 110, 110, 110, 112, 112, 112, 112, 114, 114, + 114, 114, 117, 117, 117, 117, 58, 58, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, + 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, + 84, 85, 85, 86, 86, 87, 87, 89, 89, 106, 106, 107, 107, 113, 113, + 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 38, 42, 44, 59, 88, + 90, -1, -1, 33, 33, 33, 33, 34, 34, 34, 34, 40, 40, 40, 40, + 41, 41, 41, 41, 63, 63, 63, 63, 39, 39, 43, 43, 124, 124, 35, + 62, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, + 36, 36, 36, 36, 36, 36, 64, 64, 64, 64, 64, 64, 64, 64, 91, + 91, 91, 91, 91, 91, 91, 91, 93, 93, 93, 93, 93, 93, 93, 93, + 126, 126, 126, 126, 126, 126, 126, 126, 94, 94, 94, 94, 125, 125, 125, + 125, 60, 60, 96, 96, 123, 123, -1, -1, 92, 92, 195, 195, 208, 208, + 128, 130, 131, 162, 184, 194, 224, 226, -1, -1, 153, 153, 153, 153, 153, + 153, 153, 153, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167, + 167, 167, 167, 167, 172, 172, 172, 172, 172, 172, 172, 172, 176, 176, 176, + 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 179, 179, + 179, 179, 179, 179, 179, 179, 209, 209, 209, 209, 209, 209, 209, 209, 216, + 216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 217, + 227, 227, 227, 227, 227, 227, 227, 227, 229, 229, 229, 229, 229, 229, 229, + 229, 230, 230, 230, 230, 230, 230, 230, 230, 129, 129, 129, 129, 132, 132, + 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 136, 136, 136, 136, 146, + 146, 146, 146, 154, 154, 154, 154, 156, 156, 156, 156, 160, 160, 160, 160, + 163, 163, 163, 163, 164, 164, 164, 164, 169, 169, 169, 169, 170, 170, 170, + 170, 173, 173, 173, 173, 178, 178, 178, 178, 181, 181, 181, 181, 185, 185, + 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 189, 189, 189, 189, 190, + 190, 190, 190, 196, 196, 196, 196, 198, 198, 198, 198, 228, 228, 228, 228, + 232, 232, 232, 232, 233, 233, 233, 233, 1, 1, 135, 135, 137, 137, 138, + 138, 139, 139, 140, 140, 141, 141, 143, 143, 147, 147, 149, 149, 150, 150, + 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 165, 165, 166, 166, 168, + 168, 174, 174, 175, 175, 180, 180, 182, 182, 183, 183, 188, 188, 191, 191, + 197, 197, 231, 231, 239, 239, 9, 142, 144, 145, 148, 159, 171, 206, 215, + 225, 236, 237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199, 199, + 199, 199, 199, 199, 199, 199, 207, 207, 207, 207, 207, 207, 207, 207, 234, + 234, 234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235, + 192, 192, 192, 192, 193, 193, 193, 193, 200, 200, 200, 200, 201, 201, 201, + 201, 202, 202, 202, 202, 205, 205, 205, 205, 210, 210, 210, 210, 213, 213, + 213, 213, 218, 218, 218, 218, 219, 219, 219, 219, 238, 238, 238, 238, 240, + 240, 240, 240, 242, 242, 242, 242, 243, 243, 243, 243, 255, 255, 255, 255, + 203, 203, 204, 204, 211, 211, 212, 212, 214, 214, 221, 221, 222, 222, 223, + 223, 241, 241, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 250, 250, + 251, 251, 252, 252, 253, 253, 254, 254, 2, 3, 4, 5, 6, 7, 8, + 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 127, 220, 249, -1, 10, 10, 10, 10, 13, 13, 13, + 13, 22, 22, 22, 22, 256, 256, 256, 256, + }; + + std::vector output; + int16_t decode_state; + // Parse one half byte... we leverage some lookup tables to keep the logic + // here really simple. + auto nibble = [&output, &decode_state](uint8_t nibble) { + int16_t emit = emit_sub_tbl[16 * emit_tbl[decode_state] + nibble]; + int16_t next = next_sub_tbl[16 * next_tbl[decode_state] + nibble]; + if (emit != -1) { + if (emit >= 0 && emit < 256) { + output.push_back(static_cast(emit)); + } else { + assert(emit == 256); + } + } + decode_state = next; + }; + for (auto _ : state) { + output.clear(); + decode_state = 0; + for (auto c : *kInput) { + nibble(c >> 4); + nibble(c & 0xf); + } + } +} +BENCHMARK(BM_LegacyDecode); + +// Some distros have RunSpecifiedBenchmarks under the benchmark namespace, +// and others do not. This allows us to support both modes. +namespace benchmark { +void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); } +} // namespace benchmark + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(&argc, argv); + benchmark::Initialize(&argc, argv); + benchmark::RunTheBenchmarksNamespaced(); + return 0; +} diff --git a/tools/codegen/core/BUILD b/tools/codegen/core/BUILD new file mode 100644 index 00000000000..a1078ab390b --- /dev/null +++ b/tools/codegen/core/BUILD @@ -0,0 +1,28 @@ +# Copyright 2012 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//bazel:grpc_build_system.bzl", "grpc_cc_binary") + +grpc_cc_binary( + name = "gen_huffman_decompressor", + srcs = ["gen_huffman_decompressor.cc"], + external_deps = [ + "absl/strings", + "absl/memory", + "absl/types:optional", + "absl/types:variant", + "libcrypto", + ], + deps = ["//:huffsyms"], +) diff --git a/tools/codegen/core/gen_hpack_tables.cc b/tools/codegen/core/gen_hpack_tables.cc deleted file mode 100644 index d01a90eb78f..00000000000 --- a/tools/codegen/core/gen_hpack_tables.cc +++ /dev/null @@ -1,247 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* generates constant tables for hpack.cc */ - -#include -#include -#include -#include - -#include -#include "src/core/ext/transport/chttp2/transport/huffsyms.h" - -/* - * Huffman decoder table generation - */ - -#define MAXHUFFSTATES 1024 - -/* represents a set of symbols as an array of booleans indicating inclusion */ -typedef struct { char included[GRPC_CHTTP2_NUM_HUFFSYMS]; } symset; -/* represents a lookup table indexed by a nibble */ -typedef struct { unsigned values[16]; } nibblelut; - -#define NOT_SET (~(unsigned)0) - -/* returns a symset that includes all possible symbols */ -static symset symset_all(void) { - symset x; - memset(x.included, 1, sizeof(x.included)); - return x; -} - -/* returns a symset that includes no symbols */ -static symset symset_none(void) { - symset x; - memset(x.included, 0, sizeof(x.included)); - return x; -} - -/* returns an empty nibblelut */ -static nibblelut nibblelut_empty(void) { - nibblelut x; - int i; - for (i = 0; i < 16; i++) { - x.values[i] = NOT_SET; - } - return x; -} - -/* counts symbols in a symset - only used for debug builds */ -#ifndef NDEBUG -static int nsyms(symset s) { - int i; - int c = 0; - for (i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) { - c += s.included[i] != 0; - } - return c; -} -#endif - -/* global table of discovered huffman decoding states */ -static struct { - /* the bit offset that this state starts at */ - unsigned bitofs; - /* the set of symbols that this state started with */ - symset syms; - - /* lookup table for the next state */ - nibblelut next; - /* lookup table for what to emit */ - nibblelut emit; -} huffstates[MAXHUFFSTATES]; -static unsigned nhuffstates = 0; - -/* given a number of decoded bits and a set of symbols that are live, - return the index into the decoder table for this state. - set isnew to 1 if this state was previously undiscovered */ -static unsigned state_index(unsigned bitofs, symset syms, unsigned *isnew) { - unsigned i; - for (i = 0; i < nhuffstates; i++) { - if (huffstates[i].bitofs != bitofs) continue; - if (0 != memcmp(huffstates[i].syms.included, syms.included, - GRPC_CHTTP2_NUM_HUFFSYMS)) - continue; - *isnew = 0; - return i; - } - GPR_ASSERT(nhuffstates != MAXHUFFSTATES); - - i = nhuffstates; - nhuffstates++; - - huffstates[i].bitofs = bitofs; - huffstates[i].syms = syms; - huffstates[i].next = nibblelut_empty(); - huffstates[i].emit = nibblelut_empty(); - *isnew = 1; - return i; -} - -/* recursively build a decoding table - - state - the huffman state that we are trying to fill in - nibble - the current nibble - nibbits - the number of bits in the nibble that have been filled in - bitofs - the number of bits of symbol that have been decoded - emit - the symbol to emit on this nibble (or -1 if no symbol has been - found) - syms - the set of symbols that could be matched */ -static void build_dec_tbl(unsigned state, unsigned nibble, int nibbits, - unsigned bitofs, unsigned emit, symset syms) { - unsigned i; - unsigned bit; - - /* If we have four bits in the nibble we're looking at, then we can fill in - a slot in the lookup tables. */ - if (nibbits == 4) { - unsigned isnew; - /* Find the state that we are in: this may be a new state, in which case - we recurse to fill it in, or we may have already seen this state, in - which case the recursion terminates */ - unsigned st = state_index(bitofs, syms, &isnew); - GPR_ASSERT(huffstates[state].next.values[nibble] == NOT_SET); - huffstates[state].next.values[nibble] = st; - huffstates[state].emit.values[nibble] = emit; - if (isnew) { - build_dec_tbl(st, 0, 0, bitofs, NOT_SET, syms); - } - return; - } - - assert(nsyms(syms)); - - /* A bit can be 0 or 1 */ - for (bit = 0; bit < 2; bit++) { - /* walk over active symbols and see if they have this bit set */ - symset nextsyms = symset_none(); - for (i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) { - if (!syms.included[i]) continue; /* disregard inactive symbols */ - if (((grpc_chttp2_huffsyms[i].bits >> - (grpc_chttp2_huffsyms[i].length - bitofs - 1)) & - 1) == bit) { - /* the bit is set, include it in the next recursive set */ - if (grpc_chttp2_huffsyms[i].length == bitofs + 1) { - /* additionally, we've gotten to the end of a symbol - this is a - special recursion step: re-activate all the symbols, reset - bitofs to zero, and recurse */ - build_dec_tbl(state, (nibble << 1) | bit, nibbits + 1, 0, i, - symset_all()); - /* skip the remainder of this loop */ - goto next; - } - nextsyms.included[i] = 1; - } - } - /* recurse down for this bit */ - build_dec_tbl(state, (nibble << 1) | bit, nibbits + 1, bitofs + 1, emit, - nextsyms); - next:; - } -} - -static nibblelut ctbl[MAXHUFFSTATES]; -static int nctbl; - -static int ctbl_idx(nibblelut x) { - int i; - for (i = 0; i < nctbl; i++) { - if (0 == memcmp(&x, ctbl + i, sizeof(nibblelut))) return i; - } - ctbl[i] = x; - nctbl++; - return i; -} - -static void dump_ctbl(const char *name) { - int i, j; - printf("static const gpr_int16 %s[%d*16] = {\n", name, nctbl); - for (i = 0; i < nctbl; i++) { - for (j = 0; j < 16; j++) { - printf("%d,", ctbl[i].values[j]); - } - printf("\n"); - } - printf("};\n"); -} - -static void generate_huff_tables(void) { - unsigned i; - build_dec_tbl(state_index(0, symset_all(), &i), 0, 0, 0, NOT_SET, - symset_all()); - - nctbl = 0; - printf("static const gpr_uint8 next_tbl[%d] = {", nhuffstates); - for (i = 0; i < nhuffstates; i++) { - printf("%d,", ctbl_idx(huffstates[i].next)); - } - printf("};\n"); - dump_ctbl("next_sub_tbl"); - - nctbl = 0; - printf("static const gpr_uint16 emit_tbl[%d] = {", nhuffstates); - for (i = 0; i < nhuffstates; i++) { - printf("%d,", ctbl_idx(huffstates[i].emit)); - } - printf("};\n"); - dump_ctbl("emit_sub_tbl"); -} - -static void generate_base64_huff_encoder_table(void) { - static const char alphabet[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i; - - printf( - "static const struct { gpr_uint16 bits, gpr_uint8 length } " - "base64_syms[64] = {\n"); - for (i = 0; i < 64; i++) { - printf("{0x%x, %d},", grpc_chttp2_huffsyms[(unsigned char)alphabet[i]].bits, - grpc_chttp2_huffsyms[(unsigned char)alphabet[i]].length); - } - printf("};\n"); -} - -int main(void) { - generate_huff_tables(); - generate_base64_huff_encoder_table(); - - return 0; -} diff --git a/tools/codegen/core/gen_huffman_decompressor.cc b/tools/codegen/core/gen_huffman_decompressor.cc new file mode 100644 index 00000000000..518b48195c5 --- /dev/null +++ b/tools/codegen/core/gen_huffman_decompressor.cc @@ -0,0 +1,1315 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/types/optional.h" +#include "absl/types/variant.h" + +#include "src/core/ext/transport/chttp2/transport/huffsyms.h" + +/////////////////////////////////////////////////////////////////////////////// +// SHA256 hash handling +// We need strong uniqueness checks of some very long strings - so we hash +// them with SHA256 and compare. +struct Hash { + uint8_t bytes[SHA256_DIGEST_LENGTH]; + bool operator==(const Hash& other) const { + return memcmp(bytes, other.bytes, SHA256_DIGEST_LENGTH) == 0; + } + bool operator<(const Hash& other) const { + return memcmp(bytes, other.bytes, SHA256_DIGEST_LENGTH) < 0; + } +}; + +// Given a vector of ints (T), return a Hash object with the sha256 +template +Hash HashVec(const std::vector& v) { + Hash h; + SHA1(reinterpret_cast(v.data()), v.size() * sizeof(T), + h.bytes); + return h; +} + +/////////////////////////////////////////////////////////////////////////////// +// BitQueue +// A utility that treats a sequence of bits like a queue +class BitQueue { + public: + BitQueue(unsigned mask, int len) : mask_(mask), len_(len) {} + BitQueue() : BitQueue(0, 0) {} + + // Return the most significant bit (the front of the queue) + int Front() const { return (mask_ >> (len_ - 1)) & 1; } + // Pop one bit off the queue + void Pop() { + mask_ &= ~(1 << (len_ - 1)); + len_--; + } + bool Empty() const { return len_ == 0; } + int length() const { return len_; } + unsigned mask() const { return mask_; } + + // Text representation of the queue + std::string ToString() const { + return absl::StrCat(absl::Hex(mask_), "/", len_); + } + + // Comparisons so that we can use BitQueue as a key in a std::map + bool operator<(const BitQueue& other) const { + return std::tie(mask_, len_) < std::tie(other.mask_, other.len_); + } + + private: + // The bits + unsigned mask_; + // How many bits have we + int len_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Symbol sets for the huffman tree + +// A Sym is one symbol in the tree, and the bits that we need to read to decode +// that symbol. As we progress through decoding we remove bits from the symbol, +// but also condense the number of symbols we're considering. +struct Sym { + BitQueue bits; + int symbol; + + bool operator<(const Sym& other) const { + return std::tie(bits, symbol) < std::tie(other.bits, other.symbol); + } +}; + +// A SymSet is all the symbols we're considering at some time +using SymSet = std::vector; + +// Debug utility to turn a SymSet into a string +std::string SymSetString(const SymSet& syms) { + std::vector parts; + for (const Sym& sym : syms) { + parts.push_back(absl::StrCat(sym.symbol, ":", sym.bits.ToString())); + } + return absl::StrJoin(parts, ","); +} + +// Initial SymSet - all the symbols [0..256] with their bits initialized from +// the http2 static huffman tree. +SymSet AllSyms() { + SymSet syms; + for (int i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) { + Sym sym; + sym.bits = + BitQueue(grpc_chttp2_huffsyms[i].bits, grpc_chttp2_huffsyms[i].length); + sym.symbol = i; + syms.push_back(sym); + } + return syms; +} + +// What whould we do after reading a set of bits? +struct ReadActions { + // Emit these symbols + std::vector emit; + // Number of bits that were consumed by the read + int consumed; + // Remaining SymSet that we need to consider on the next read action + SymSet remaining; +}; + +// Given a SymSet \a pending, read through the bits in \a index and determine +// what actions the decoder should take. +// allow_multiple controls the behavior should we get to the last bit in pending +// and hence know which symbol to emit, but we still have bits in index. +// We could either start decoding the next symbol (allow_multiple == true), or +// we could stop (allow_multiple == false). +// If allow_multiple is true we tend to emit more per read op, but generate +// bigger tables. +ReadActions ActionsFor(BitQueue index, SymSet pending, bool allow_multiple) { + std::vector emit; + int len_start = index.length(); + int len_consume = len_start; + + // We read one bit in index at a time, so whilst we have bits... + while (!index.Empty()) { + SymSet next_pending; + // For each symbol in the pending set + for (auto sym : pending) { + // If the first bit doesn't match, then that symbol is not part of our + // remaining set. + if (sym.bits.Front() != index.Front()) continue; + sym.bits.Pop(); + next_pending.push_back(sym); + } + switch (next_pending.size()) { + case 0: + // There should be no bit patterns that are undecodable. + abort(); + case 1: + // If we have one symbol left, we need to have decoded all of it. + if (!next_pending[0].bits.Empty()) abort(); + // Emit that symbol + emit.push_back(next_pending[0].symbol); + // Track how many bits we've read. + len_consume = index.length() - 1; + // If we allow multiple, reprime pending and continue, otherwise stop. + if (!allow_multiple) goto done; + pending = AllSyms(); + break; + default: + pending = std::move(next_pending); + break; + } + // Finished with this bit, continue with next + index.Pop(); + } +done: + return ReadActions{std::move(emit), len_start - len_consume, pending}; +} + +/////////////////////////////////////////////////////////////////////////////// +// MatchCase +// A variant that helps us bunch together related ReadActions + +// A Matched in a MatchCase indicates that we need to emit some number of +// symbols +struct Matched { + // number of symbols to emit + int emits; + + bool operator<(const Matched& other) const { return emits < other.emits; } +}; + +// Unmatched says we didn't emit anything and we need to keep decoding +struct Unmatched { + SymSet syms; + + bool operator<(const Unmatched& other) const { return syms < other.syms; } +}; + +// Emit end of stream +struct End { + bool operator<(End) const { return false; } +}; + +using MatchCase = absl::variant; + +/////////////////////////////////////////////////////////////////////////////// +// Text & numeric helper functions + +// Given a vector of lines, indent those lines by some number of indents +// (2 spaces) and return that. +std::vector IndentLines(std::vector lines, + int n = 1) { + std::string indent(2 * n, ' '); + for (auto& line : lines) { + line = absl::StrCat(indent, line); + } + return lines; +} + +// Given a snake_case_name return a PascalCaseName +std::string ToPascalCase(const std::string& in) { + std::string out; + bool next_upper = true; + for (char c : in) { + if (c == '_') { + next_upper = true; + } else { + if (next_upper) { + out.push_back(toupper(c)); + next_upper = false; + } else { + out.push_back(c); + } + } + } + return out; +} + +// Return a uint type for some number of bits (16 -> uint16_t, 32 -> uint32_t) +std::string Uint(int bits) { return absl::StrCat("uint", bits, "_t"); } + +// Given a maximum value, how many bits to store it in a uint +int TypeBitsForMax(int max) { + if (max <= 255) { + return 8; + } else if (max <= 65535) { + return 16; + } else { + return 32; + } +} + +// Combine Uint & TypeBitsForMax to make for more concise code +std::string TypeForMax(int max) { return Uint(TypeBitsForMax(max)); } + +// How many bits are needed to encode a value +int BitsForMaxValue(int x) { + int n = 0; + while (x >= (1 << n)) n++; + return n; +} + +/////////////////////////////////////////////////////////////////////////////// +// Codegen framework +// Some helpers so we don't need to generate all the code linearly, which helps +// organize this a little more nicely. + +// An Item is our primitive for code generation, it can generate some lines +// that it would like to emit - those lines are fed to a parent item that might +// generate more lines or mutate the ones we return, and so on until codegen +// is complete. +class Item { + public: + virtual ~Item() = default; + virtual std::vector ToLines() const = 0; + std::string ToString() const { + return absl::StrCat(absl::StrJoin(ToLines(), "\n"), "\n"); + } +}; +using ItemPtr = std::unique_ptr; + +// An item that emits one line (the one given as an argument!) +class String : public Item { + public: + explicit String(std::string s) : s_(std::move(s)) {} + std::vector ToLines() const override { return {s_}; } + + private: + std::string s_; +}; + +// An item that returns a fixed copyright notice and autogenerated note text. +class Prelude final : public Item { + public: + std::vector ToLines() const { + return { + "// Copyright 2022 gRPC authors.", + "//", + "// Licensed under the Apache License, Version 2.0 (the " + "\"License\");", + "// you may not use this file except in compliance with the License.", + "// You may obtain a copy of the License at", + "//", + "// http://www.apache.org/licenses/LICENSE-2.0", + "//", + "// Unless required by applicable law or agreed to in writing, " + "software", + "// distributed under the License is distributed on an \"AS IS\" " + "BASIS,", + "// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or " + "implied.", + "// See the License for the specific language governing permissions " + "and", + "// limitations under the License.", + "", + std::string(80, '/'), + "// This file is autogenerated: see " + "tools/codegen/core/gen_huffman_decompressor.cc", + ""}; + } +}; + +class Switch; + +// A Sink is an Item that we can add more Items to. +// At codegen time it calls each of its children in turn and concatenates +// their results together. +class Sink : public Item { + public: + std::vector ToLines() const override { + std::vector lines; + for (const auto& item : children_) { + for (const auto& line : item->ToLines()) { + lines.push_back(line); + } + } + return lines; + } + + // Add one string to our output. + void Add(std::string s) { + children_.push_back(absl::make_unique(std::move(s))); + } + + // Add an item of type T to our output (constructing it with args). + template + T* Add(Args&&... args) { + auto v = absl::make_unique(std::forward(args)...); + auto* r = v.get(); + children_.push_back(std::move(v)); + return r; + } + + private: + std::vector children_; +}; + +// A sink that indents its lines by one indent (2 spaces) +class Indent : public Sink { + public: + std::vector ToLines() const override { + return IndentLines(Sink::ToLines()); + } +}; + +// A Sink that wraps its lines in a while block +class While : public Sink { + public: + explicit While(std::string cond) : cond_(std::move(cond)) {} + std::vector ToLines() const override { + std::vector lines; + lines.push_back(absl::StrCat("while (", cond_, ") {")); + for (const auto& line : IndentLines(Sink::ToLines())) { + lines.push_back(line); + } + lines.push_back("}"); + return lines; + } + + private: + std::string cond_; +}; + +// A switch statement. +// Cases can be modified by calling the Case member. +// Identical cases are collapsed into 'case X: case Y:' type blocks. +class Switch : public Item { + public: + // \a cond is the condition to place at the head of the switch statement. + // eg. "switch (cond) {". + explicit Switch(std::string cond) : cond_(std::move(cond)) {} + std::vector ToLines() const override { + std::map> reverse_map; + for (const auto& kv : cases_) { + reverse_map[kv.second.ToString()].push_back(kv.first); + } + std::vector lines; + lines.push_back(absl::StrCat("switch (", cond_, ") {")); + for (const auto& kv : reverse_map) { + for (const auto& cond : kv.second) { + lines.push_back(absl::StrCat(" case ", cond, ":")); + } + lines.back().append(" {"); + for (const auto& case_line : + IndentLines(cases_.find(kv.second[0])->second.ToLines(), 2)) { + lines.push_back(case_line); + } + lines.push_back(" }"); + } + lines.push_back("}"); + return lines; + } + + Sink* Case(std::string cond) { return &cases_[cond]; } + + private: + std::string cond_; + std::map cases_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// BuildCtx declaration +// Shared state for one code gen attempt + +class TableBuilder; +class FunMaker; + +class BuildCtx { + public: + BuildCtx(std::vector max_bits_for_depth, Sink* global_fns, + Sink* global_decls, Sink* global_values, FunMaker* fun_maker) + : max_bits_for_depth_(std::move(max_bits_for_depth)), + global_fns_(global_fns), + global_decls_(global_decls), + global_values_(global_values), + fun_maker_(fun_maker) {} + + void AddStep(SymSet start_syms, int num_bits, bool is_top, bool refill, + int depth, Sink* out); + void AddMatchBody(TableBuilder* table_builder, std::string index, + std::string ofs, const MatchCase& match_case, bool is_top, + bool refill, int depth, Sink* out); + void AddDone(SymSet start_syms, int num_bits, bool all_ones_so_far, + Sink* out); + + int NewId() { return next_id_++; } + int MaxBitsForTop() const { return max_bits_for_depth_[0]; } + + absl::optional PreviousNameForArtifact(std::string proposed_name, + Hash hash) { + auto it = arrays_.find(hash); + if (it == arrays_.end()) { + arrays_.emplace(hash, proposed_name); + return absl::nullopt; + } + return it->second; + } + + Sink* global_fns() const { return global_fns_; } + Sink* global_decls() const { return global_decls_; } + Sink* global_values() const { return global_values_; } + + private: + const std::vector max_bits_for_depth_; + std::map arrays_; + int next_id_ = 1; + Sink* const global_fns_; + Sink* const global_decls_; + Sink* const global_values_; + FunMaker* const fun_maker_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// TableBuilder +// All our magic for building decode tables. +// We have three kinds of tables to generate: +// 1. op tables that translate a bit sequence to which decode case we should +// execute (and arguments to it), and +// 2. emit tables that translate an index given by the op table and tell us +// which symbols to emit +// Op table format +// Our opcodes contain an offset into an emit table, a number of bits consumed +// and an operation. The consumed bits are how many of the presented to us bits +// we actually took. The operation tells whether to emit some symbols (and how +// many) or to keep decoding. +// Optimization 1: +// op tables are essentially dense maps of bits -> opcode, and it turns out +// that *many* of the opcodes repeat across index bits for some of our tables +// so for those we split the table into two levels: first level indexes into +// a child table, and the child table contains the deduped opcodes. +// Optimization 2: +// Emit tables are a bit list of uint8_ts, and are indexed into by the op +// table (with an offset and length) - since many symbols get repeated, we try +// to overlay the symbols in the emit table to reduce the size. +// Optimization 3: +// We shard the table into some number of slices and use the top bits of the +// incoming lookup to select the shard. This tends to allow us to use smaller +// types to represent the table, saving on footprint. + +class TableBuilder { + public: + explicit TableBuilder(BuildCtx* ctx) : ctx_(ctx), id_(ctx->NewId()) {} + + // Append one case to the table + void Add(int match_case, std::vector emit, int consumed_bits) { + elems_.push_back({match_case, std::move(emit), consumed_bits}); + max_consumed_bits_ = std::max(max_consumed_bits_, consumed_bits); + max_match_case_ = std::max(max_match_case_, match_case); + } + + // Build the table + void Build() const { + Choose()->Build(this, BitsForMaxValue(elems_.size() - 1)); + } + + // Generate a call to the accessor function for the emit table + std::string EmitAccessor(std::string index, std::string offset) { + return absl::StrCat("GetEmit", id_, "(", index, ", ", offset, ")"); + } + + // Generate a call to the accessor function for the op table + std::string OpAccessor(std::string index) { + return absl::StrCat("GetOp", id_, "(", index, ")"); + } + + int ConsumeBits() const { return BitsForMaxValue(max_consumed_bits_); } + int MatchBits() const { return BitsForMaxValue(max_match_case_); } + + private: + // One element in the op table. + struct Elem { + int match_case; + std::vector emit; + int consumed_bits; + }; + + // A nested slice is one slice of a table using two level lookup + // - i.e. we look at an outer table to get an index into the inner table, + // and then fetch the result from there. + struct NestedSlice { + std::vector emit; + std::vector inner; + std::vector outer; + + // Various sizes return number of bits to be generated + + size_t InnerSize() const { + return inner.size() * + TypeBitsForMax(*std::max_element(inner.begin(), inner.end())); + } + + size_t OuterSize() const { + return outer.size() * + TypeBitsForMax(*std::max_element(outer.begin(), outer.end())); + } + + size_t EmitSize() const { return emit.size() * 8; } + }; + + // A slice is one part of a larger table. + struct Slice { + std::vector emit; + std::vector ops; + + // Various sizes return number of bits to be generated + + size_t OpsSize() const { + return ops.size() * + TypeBitsForMax(*std::max_element(ops.begin(), ops.end())); + } + + size_t EmitSize() const { return emit.size() * 8; } + + // Given a vector of symbols to emit, return the offset into the emit table + // that they're at (adding them to the emit table if necessary). + int OffsetOf(const std::vector& x) { + if (x.empty()) return 0; + auto r = std::search(emit.begin(), emit.end(), x.begin(), x.end()); + if (r == emit.end()) { + // look for a partial match @ end + for (size_t check_len = x.size() - 1; check_len > 0; check_len--) { + if (emit.size() < check_len) continue; + bool matches = true; + for (size_t i = 0; matches && i < check_len; i++) { + if (emit[emit.size() - check_len + i] != x[i]) matches = false; + } + if (matches) { + int offset = emit.size() - check_len; + for (size_t i = check_len; i < x.size(); i++) { + emit.push_back(x[i]); + } + return offset; + } + } + // add new + int result = emit.size(); + for (auto v : x) emit.push_back(v); + return result; + } + return r - emit.begin(); + } + + // Convert this slice to a nested slice. + NestedSlice MakeNestedSlice() const { + NestedSlice result; + result.emit = emit; + std::map op_to_inner; + for (auto v : ops) { + auto it = op_to_inner.find(v); + if (it == op_to_inner.end()) { + it = op_to_inner.emplace(v, op_to_inner.size()).first; + result.inner.push_back(v); + } + result.outer.push_back(it->second); + } + return result; + } + }; + + // An EncodeOption is a potential way of encoding a table. + struct EncodeOption { + // Overall size (in bits) of the table encoding + virtual size_t Size() const = 0; + // Generate the code + virtual void Build(const TableBuilder* builder, int op_bits) const = 0; + virtual ~EncodeOption() {} + }; + + // NestedTable is a table that uses two level lookup for each slice + struct NestedTable : public EncodeOption { + std::vector slices; + int slice_bits; + size_t Size() const override { + size_t sum = 0; + std::vector h_emit; + std::vector h_inner; + std::vector h_outer; + for (size_t i = 0; i < slices.size(); i++) { + h_emit.push_back(HashVec(slices[i].emit)); + h_inner.push_back(HashVec(slices[i].inner)); + h_outer.push_back(HashVec(slices[i].outer)); + } + std::set seen; + for (size_t i = 0; i < slices.size(); i++) { + // Try to account for deduplication in the size calculation. + if (seen.count(h_emit[i]) == 0) sum += slices[i].EmitSize(); + if (seen.count(h_outer[i]) == 0) sum += slices[i].OuterSize(); + if (seen.count(h_inner[i]) == 0) sum += slices[i].OuterSize(); + seen.insert(h_emit[i]); + seen.insert(h_outer[i]); + seen.insert(h_inner[i]); + } + if (slice_bits != 0) sum += 3 * 64 * slices.size(); + return sum; + } + void Build(const TableBuilder* builder, int op_bits) const override { + Sink* const global_fns = builder->ctx_->global_fns(); + Sink* const global_decls = builder->ctx_->global_decls(); + Sink* const global_values = builder->ctx_->global_values(); + const int id = builder->id_; + std::vector lines; + const uint64_t max_inner = MaxInner(); + const uint64_t max_outer = MaxOuter(); + std::vector emit_names; + std::vector inner_names; + std::vector outer_names; + for (size_t i = 0; i < slices.size(); i++) { + emit_names.push_back(builder->GenArray( + absl::StrCat("table", id, "_", i, "_emit"), "uint8_t", + slices[i].emit, true, global_decls, global_values)); + inner_names.push_back(builder->GenArray( + absl::StrCat("table", id, "_", i, "_inner"), TypeForMax(max_inner), + slices[i].inner, true, global_decls, global_values)); + outer_names.push_back(builder->GenArray( + absl::StrCat("table", id, "_", i, "_outer"), TypeForMax(max_outer), + slices[i].outer, false, global_decls, global_values)); + } + if (slice_bits == 0) { + global_fns->Add(absl::StrCat("static inline uint64_t GetOp", id, + "(size_t i) { return ", inner_names[0], + "[", outer_names[0], "[i]]; }")); + global_fns->Add(absl::StrCat("static inline uint64_t GetEmit", id, + "(size_t, size_t emit) { return ", + emit_names[0], "[emit]; }")); + } else { + GenCompound(id, emit_names, "emit", "uint8_t", global_decls, + global_values); + GenCompound(id, inner_names, "inner", TypeForMax(max_inner), + global_decls, global_values); + GenCompound(id, outer_names, "outer", TypeForMax(max_outer), + global_decls, global_values); + global_fns->Add(absl::StrCat( + "static inline uint64_t GetOp", id, "(size_t i) { return table", id, + "_inner_[i >> ", op_bits - slice_bits, "][table", id, + "_outer_[i >> ", op_bits - slice_bits, "][i & 0x", + absl::Hex((1 << (op_bits - slice_bits)) - 1), "]]; }")); + global_fns->Add(absl::StrCat("static inline uint64_t GetEmit", id, + "(size_t i, size_t emit) { return table", + id, "_emit_[i >> ", op_bits - slice_bits, + "][emit]; }")); + } + } + uint64_t MaxInner() const { + uint64_t max_inner = 0; + for (size_t i = 0; i < slices.size(); i++) { + max_inner = std::max( + max_inner, + *std::max_element(slices[i].inner.begin(), slices[i].inner.end())); + } + return max_inner; + } + int MaxOuter() const { + int max_outer = 0; + for (size_t i = 0; i < slices.size(); i++) { + max_outer = std::max( + max_outer, + *std::max_element(slices[i].outer.begin(), slices[i].outer.end())); + } + return max_outer; + } + }; + + // Encoding that uses single level lookup for each slice. + struct Table : public EncodeOption { + std::vector slices; + int slice_bits; + size_t Size() const override { + size_t sum = 0; + std::vector h_emit; + std::vector h_ops; + for (size_t i = 0; i < slices.size(); i++) { + h_emit.push_back(HashVec(slices[i].emit)); + h_ops.push_back(HashVec(slices[i].ops)); + } + std::set seen; + for (size_t i = 0; i < slices.size(); i++) { + if (seen.count(h_emit[i]) == 0) sum += slices[i].EmitSize(); + if (seen.count(h_ops[i]) == 0) sum += slices[i].OpsSize(); + seen.insert(h_emit[i]); + seen.insert(h_ops[i]); + } + return sum + 3 * 64 * slices.size(); + } + void Build(const TableBuilder* builder, int op_bits) const override { + Sink* const global_fns = builder->ctx_->global_fns(); + Sink* const global_decls = builder->ctx_->global_decls(); + Sink* const global_values = builder->ctx_->global_values(); + uint64_t max_op = MaxOp(); + const int id = builder->id_; + std::vector emit_names; + std::vector ops_names; + for (size_t i = 0; i < slices.size(); i++) { + emit_names.push_back(builder->GenArray( + absl::StrCat("table", id, "_", i, "_emit"), "uint8_t", + slices[i].emit, true, global_decls, global_values)); + ops_names.push_back(builder->GenArray( + absl::StrCat("table", id, "_", i, "_ops"), TypeForMax(max_op), + slices[i].ops, true, global_decls, global_values)); + } + if (slice_bits == 0) { + global_fns->Add(absl::StrCat("static inline uint64_t GetOp", id, + "(size_t i) { return ", ops_names[0], + "[i]; }")); + global_fns->Add(absl::StrCat("static inline uint64_t GetEmit", id, + "(size_t, size_t emit) { return ", + emit_names[0], "[emit]; }")); + } else { + GenCompound(id, emit_names, "emit", "uint8_t", global_decls, + global_values); + GenCompound(id, ops_names, "ops", TypeForMax(max_op), global_decls, + global_values); + global_fns->Add(absl::StrCat( + "static inline uint64_t GetOp", id, "(size_t i) { return table", id, + "_ops_[i >> ", op_bits - slice_bits, "][i & 0x", + absl::Hex((1 << (op_bits - slice_bits)) - 1), "]; }")); + global_fns->Add(absl::StrCat("static inline uint64_t GetEmit", id, + "(size_t i, size_t emit) { return table", + id, "_emit_[i >> ", op_bits - slice_bits, + "][emit]; }")); + } + } + uint64_t MaxOp() const { + uint64_t max_op = 0; + for (size_t i = 0; i < slices.size(); i++) { + max_op = std::max(max_op, *std::max_element(slices[i].ops.begin(), + slices[i].ops.end())); + } + return max_op; + } + // Convert to a two-level lookup + std::unique_ptr MakeNestedTable() { + std::unique_ptr result(new NestedTable); + result->slice_bits = slice_bits; + for (const auto& slice : slices) { + result->slices.push_back(slice.MakeNestedSlice()); + } + return result; + } + }; + + // Given a number of slices (2**slice_bits), generate a table that uses a + // single level lookup for each slice based on our input. + std::unique_ptr MakeTable(size_t slice_bits) const { + std::unique_ptr
table = absl::make_unique
(); + int slices = 1 << slice_bits; + table->slices.resize(slices); + table->slice_bits = slice_bits; + const int pack_consume_bits = ConsumeBits(); + const int pack_match_bits = MatchBits(); + for (size_t i = 0; i < slices; i++) { + auto& slice = table->slices[i]; + for (size_t j = 0; j < elems_.size() / slices; j++) { + const auto& elem = elems_[i * elems_.size() / slices + j]; + slice.ops.push_back(elem.consumed_bits | + (elem.match_case << pack_consume_bits) | + (slice.OffsetOf(elem.emit) + << (pack_consume_bits + pack_match_bits))); + } + } + return table; + } + + // Helper to generate a compound table (an array of arrays) + static void GenCompound(int id, std::vector names, + std::string ext, std::string type, Sink* global_decls, + Sink* global_values) { + global_decls->Add(absl::StrCat("static const ", type, "* const table", id, + "_", ext, "_[", names.size(), "];")); + global_values->Add(absl::StrCat("const ", type, + "* const HuffDecoderCommon::table", id, "_", + ext, "_[", names.size(), "] = {")); + for (const std::string& name : names) { + global_values->Add(absl::StrCat(" ", name, ",")); + } + global_values->Add("};"); + } + + // Helper to generate an array of values + template + std::string GenArray(std::string name, std::string type, + const std::vector& values, bool hex, + Sink* global_decls, Sink* global_values) const { + auto previous_name = ctx_->PreviousNameForArtifact(name, HashVec(values)); + if (previous_name.has_value()) { + return absl::StrCat(*previous_name, "_"); + } + std::vector elems; + elems.reserve(values.size()); + for (const auto& elem : values) { + if (hex) { + if (type == "uint8_t") { + elems.push_back(absl::StrCat("0x", absl::Hex(elem, absl::kZeroPad2))); + } else if (type == "uint16_t") { + elems.push_back(absl::StrCat("0x", absl::Hex(elem, absl::kZeroPad4))); + } else { + elems.push_back(absl::StrCat("0x", absl::Hex(elem, absl::kZeroPad8))); + } + } else { + elems.push_back(absl::StrCat(elem)); + } + } + std::string data = absl::StrJoin(elems, ", "); + global_decls->Add(absl::StrCat("static const ", type, " ", name, "_[", + values.size(), "];")); + global_values->Add(absl::StrCat("const ", type, " HuffDecoderCommon::", + name, "_[", values.size(), "] = {")); + global_values->Add(absl::StrCat(" ", data)); + global_values->Add("};"); + return absl::StrCat(name, "_"); + } + + // Choose an encoding for this set of tables. + // We try all available values for slice count and choose the one that gives + // the smallest footprint. + std::unique_ptr Choose() const { + std::unique_ptr chosen; + size_t best_size = std::numeric_limits::max(); + for (size_t slice_bits = 0; (1 << slice_bits) < elems_.size(); + slice_bits++) { + auto raw = MakeTable(slice_bits); + size_t raw_size = raw->Size(); + auto nested = raw->MakeNestedTable(); + size_t nested_size = nested->Size(); + if (raw_size < best_size) { + chosen = std::move(raw); + best_size = raw_size; + } + if (nested_size < best_size) { + chosen = std::move(nested); + best_size = nested_size; + } + } + return chosen; + } + + BuildCtx* const ctx_; + std::vector elems_; + int max_consumed_bits_ = 0; + int max_match_case_ = 0; + const int id_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// FunMaker +// Handles generating the code for various functions. + +class FunMaker { + public: + explicit FunMaker(Sink* sink) : sink_(sink) {} + + // Generate a refill function - that ensures the incoming bitmask has enough + // bits for the next step. + std::string RefillTo(int n) { + if (have_refills_.count(n) == 0) { + have_refills_.insert(n); + auto fn = NewFun(absl::StrCat("RefillTo", n), "bool"); + auto s = fn->Add("buffer_len_"); + for (int i = 0; i < n; i++) { + auto c = s->Case(absl::StrCat(i)); + const int bytes_needed = (n - i + 7) / 8; + c->Add(absl::StrCat("return ", ReadBytes(bytes_needed), ";")); + } + fn->Add("return true;"); + } + return absl::StrCat("RefillTo", n, "()"); + } + + // At callsite, generate a call to a new function with base name + // base_name (new functions get a suffix of how many instances of base_name + // there have been). + // Return a sink to fill in the body of the new function. + Sink* CallNewFun(std::string base_name, Sink* callsite) { + std::string name = absl::StrCat(base_name, have_funs_[base_name]++); + callsite->Add(absl::StrCat(name, "();")); + return NewFun(name, "void"); + } + + private: + Sink* NewFun(std::string name, std::string returns) { + sink_->Add(absl::StrCat(returns, " ", name, "() {")); + auto fn = sink_->Add(); + sink_->Add("}"); + return fn; + } + + // Bring in some number of bytes from the input stream to our current read + // bits. + std::string ReadBytes(int bytes_needed) { + if (have_reads_.count(bytes_needed) == 0) { + have_reads_.insert(bytes_needed); + auto fn = NewFun(absl::StrCat("Read", bytes_needed), "bool"); + fn->Add(absl::StrCat("if (end_ - begin_ < ", bytes_needed, + ") return false;")); + fn->Add(absl::StrCat("buffer_ <<= ", 8 * bytes_needed, ";")); + for (int i = 0; i < bytes_needed; i++) { + fn->Add(absl::StrCat("buffer_ |= static_cast(*begin_++) << ", + 8 * (bytes_needed - i - 1), ";")); + } + fn->Add(absl::StrCat("buffer_len_ += ", 8 * bytes_needed, ";")); + fn->Add("return true;"); + } + return absl::StrCat("Read", bytes_needed, "()"); + } + + std::set have_refills_; + std::set have_reads_; + std::map have_funs_; + Sink* sink_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// BuildCtx implementation + +void BuildCtx::AddDone(SymSet start_syms, int num_bits, bool all_ones_so_far, + Sink* out) { + out->Add("done_ = true;"); + if (num_bits == 1) { + if (!all_ones_so_far) out->Add("ok_ = false;"); + return; + } + // we must have 0 < buffer_len_ < num_bits + auto s = out->Add("buffer_len_"); + auto c0 = s->Case("0"); + if (!all_ones_so_far) c0->Add("ok_ = false;"); + c0->Add("return;"); + for (int i = 1; i < num_bits; i++) { + auto c = s->Case(absl::StrCat(i)); + SymSet maybe; + for (auto sym : start_syms) { + if (sym.bits.length() > i) continue; + maybe.push_back(sym); + } + if (maybe.empty()) { + if (all_ones_so_far) { + c->Add("ok_ = (buffer_ & ((1<Add("ok_ = false;"); + } + c->Add("return;"); + continue; + } + TableBuilder table_builder(this); + enum Cases { + kNoEmitOk, + kFail, + kEmitOk, + }; + for (size_t n = 0; n < (1 << i); n++) { + if (all_ones_so_far && n == (1 << i) - 1) { + table_builder.Add(kNoEmitOk, {}, 0); + goto next; + } + for (auto sym : maybe) { + if ((n >> (i - sym.bits.length())) == sym.bits.mask()) { + for (int j = 0; j < (i - sym.bits.length()); j++) { + if ((n & (1 << j)) == 0) { + table_builder.Add(kFail, {}, 0); + goto next; + } + } + table_builder.Add(kEmitOk, {static_cast(sym.symbol)}, 0); + goto next; + } + } + table_builder.Add(kFail, {}, 0); + next:; + } + table_builder.Build(); + c->Add(absl::StrCat("const auto index = buffer_ & ", (1 << i) - 1, ";")); + c->Add(absl::StrCat("const auto op = ", table_builder.OpAccessor("index"), + ";")); + if (table_builder.ConsumeBits() != 0) { + fprintf(stderr, "consume bits = %d\n", table_builder.ConsumeBits()); + abort(); + } + auto s_fin = c->Add( + absl::StrCat("op & ", (1 << table_builder.MatchBits()) - 1)); + auto emit_ok = s_fin->Case(absl::StrCat(kEmitOk)); + emit_ok->Add(absl::StrCat( + "sink_(", + table_builder.EmitAccessor( + "index", absl::StrCat("op >>", table_builder.MatchBits())), + ");")); + emit_ok->Add("break;"); + auto fail = s_fin->Case(absl::StrCat(kFail)); + fail->Add("ok_ = false;"); + fail->Add("break;"); + c->Add("return;"); + } +} + +void BuildCtx::AddStep(SymSet start_syms, int num_bits, bool is_top, + bool refill, int depth, Sink* out) { + TableBuilder table_builder(this); + if (refill) { + out->Add(absl::StrCat("if (!", fun_maker_->RefillTo(num_bits), ") {")); + auto ifblk = out->Add(); + if (!is_top) { + Sym some = start_syms[0]; + auto sym = grpc_chttp2_huffsyms[some.symbol]; + int consumed_len = (sym.length - some.bits.length()); + uint32_t consumed_mask = sym.bits >> some.bits.length(); + bool all_ones_so_far = consumed_mask == ((1 << consumed_len) - 1); + AddDone(start_syms, num_bits, all_ones_so_far, + fun_maker_->CallNewFun("Done", ifblk)); + ifblk->Add("return;"); + } else { + AddDone(start_syms, num_bits, true, + fun_maker_->CallNewFun("Done", ifblk)); + ifblk->Add("break;"); + } + out->Add("}"); + } + out->Add(absl::StrCat("const auto index = (buffer_ >> (buffer_len_ - ", + num_bits, ")) & 0x", absl::Hex((1 << num_bits) - 1), + ";")); + std::map match_cases; + for (int i = 0; i < (1 << num_bits); i++) { + auto actions = ActionsFor(BitQueue(i, num_bits), start_syms, is_top); + auto add_case = [&match_cases](MatchCase match_case) { + if (match_cases.find(match_case) == match_cases.end()) { + match_cases[match_case] = match_cases.size(); + } + return match_cases[match_case]; + }; + if (actions.emit.size() == 1 && actions.emit[0] == 256) { + table_builder.Add(add_case(End{}), {}, actions.consumed); + } else if (actions.consumed == 0) { + table_builder.Add(add_case(Unmatched{std::move(actions.remaining)}), {}, + num_bits); + } else { + std::vector emit; + for (auto sym : actions.emit) emit.push_back(sym); + table_builder.Add( + add_case(Matched{static_cast(actions.emit.size())}), + std::move(emit), actions.consumed); + } + } + table_builder.Build(); + out->Add( + absl::StrCat("const auto op = ", table_builder.OpAccessor("index"), ";")); + out->Add(absl::StrCat("const int consumed = op & ", + (1 << table_builder.ConsumeBits()) - 1, ";")); + out->Add("buffer_len_ -= consumed;"); + out->Add(absl::StrCat("const auto emit_ofs = op >> ", + table_builder.ConsumeBits() + table_builder.MatchBits(), + ";")); + if (match_cases.size() == 1) { + AddMatchBody(&table_builder, "index", "emit_ofs", + match_cases.begin()->first, is_top, refill, depth, out); + } else { + auto s = out->Add( + absl::StrCat("(op >> ", table_builder.ConsumeBits(), ") & ", + (1 << table_builder.MatchBits()) - 1)); + for (auto kv : match_cases) { + auto c = s->Case(absl::StrCat(kv.second)); + AddMatchBody(&table_builder, "index", "emit_ofs", kv.first, is_top, + refill, depth, c); + c->Add("break;"); + } + } +} + +void BuildCtx::AddMatchBody(TableBuilder* table_builder, std::string index, + std::string ofs, const MatchCase& match_case, + bool is_top, bool refill, int depth, Sink* out) { + if (absl::holds_alternative(match_case)) { + out->Add("begin_ = end_;"); + out->Add("buffer_len_ = 0;"); + return; + } + if (auto* p = absl::get_if(&match_case)) { + if (refill) { + int max_bits = 0; + for (auto sym : p->syms) max_bits = std::max(max_bits, sym.bits.length()); + AddStep(p->syms, + depth + 1 >= max_bits_for_depth_.size() + ? max_bits + : std::min(max_bits, max_bits_for_depth_[depth + 1]), + false, true, depth + 1, + fun_maker_->CallNewFun("DecodeStep", out)); + } + return; + } + const auto& matched = absl::get(match_case); + for (int i = 0; i < matched.emits; i++) { + out->Add(absl::StrCat( + "sink_(", + table_builder->EmitAccessor(index, absl::StrCat(ofs, " + ", i)), ");")); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Driver code + +// Generated header and source code +struct BuildOutput { + std::string header; + std::string source; +}; + +// Given max_bits_for_depth = {n1,n2,n3,...} +// Build a decoder that first considers n1 bits, then n2, then n3, ... +BuildOutput Build(std::vector max_bits_for_depth) { + auto hdr = absl::make_unique(); + auto src = absl::make_unique(); + hdr->Add(); + src->Add(); + hdr->Add("#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_DECODE_HUFF_H"); + hdr->Add("#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_DECODE_HUFF_H"); + src->Add( + "#include \"src/core/ext/transport/chttp2/transport/decode_huff.h\""); + hdr->Add("#include "); + hdr->Add("#include "); + src->Add("#include "); + hdr->Add("#include "); + hdr->Add( + absl::StrCat("// GEOMETRY: ", absl::StrJoin(max_bits_for_depth, ","))); + hdr->Add("namespace grpc_core {"); + src->Add("namespace grpc_core {"); + hdr->Add("class HuffDecoderCommon {"); + hdr->Add(" protected:"); + auto global_fns = hdr->Add(); + hdr->Add(" private:"); + auto global_decls = hdr->Add(); + hdr->Add("};"); + hdr->Add( + "template class HuffDecoder : public HuffDecoderCommon {"); + hdr->Add(" public:"); + auto pub = hdr->Add(); + hdr->Add(" private:"); + auto prv = hdr->Add(); + FunMaker fun_maker(prv->Add()); + hdr->Add("};"); + hdr->Add("} // namespace grpc_core"); + hdr->Add("#endif // GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_DECODE_HUFF_H"); + auto global_values = src->Add(); + src->Add("} // namespace grpc_core"); + BuildCtx ctx(std::move(max_bits_for_depth), global_fns, global_decls, + global_values, &fun_maker); + // constructor + pub->Add( + "HuffDecoder(F sink, const uint8_t* begin, const uint8_t* end) : " + "sink_(sink), begin_(begin), end_(end) {}"); + // members + prv->Add("F sink_;"); + prv->Add("const uint8_t* begin_;"); + prv->Add("const uint8_t* const end_;"); + prv->Add("uint64_t buffer_ = 0;"); + prv->Add("int buffer_len_ = 0;"); + prv->Add("bool ok_ = true;"); + prv->Add("bool done_ = false;"); + // main fn + pub->Add("bool Run() {"); + auto body = pub->Add(); + body->Add("while (!done_) {"); + ctx.AddStep(AllSyms(), ctx.MaxBitsForTop(), true, true, 0, + body->Add()); + body->Add("}"); + body->Add("return ok_;"); + pub->Add("}"); + return {hdr->ToString(), src->ToString()}; +} + +// Generate all permutations of max_bits_for_depth for the Build function, +// with a minimum step size of 5 bits (needed for http2 I think) and a +// configurable maximum step size. +class PermutationBuilder { + public: + explicit PermutationBuilder(int max_depth) : max_depth_(max_depth) {} + std::vector> Run() { + Step({}); + return std::move(perms_); + } + + private: + void Step(std::vector so_far) { + int sum_so_far = std::accumulate(so_far.begin(), so_far.end(), 0); + if (so_far.size() > max_depth_ || + (so_far.size() == max_depth_ && sum_so_far != 30)) { + return; + } + if (sum_so_far + 5 > 30) { + perms_.emplace_back(std::move(so_far)); + return; + } + for (int i = 5; i <= std::min(30 - sum_so_far, 16); i++) { + auto p = so_far; + p.push_back(i); + Step(std::move(p)); + } + } + + const int max_depth_; + std::vector> perms_; +}; + +// Does what it says. +void WriteFile(std::string filename, std::string content) { + std::ofstream ofs(filename); + ofs << content; +} + +int main(void) { + BuildOutput best; + size_t best_len = std::numeric_limits::max(); + std::vector> results; + std::queue threads; + // Generate all permutations of max_bits_for_depth for the Build function. + // Then generate all variations of the code. + for (const auto& perm : PermutationBuilder(30).Run()) { + while (threads.size() > 200) { + threads.front().join(); + threads.pop(); + } + results.emplace_back(absl::make_unique()); + threads.emplace([perm, r = results.back().get()] { *r = Build(perm); }); + } + while (!threads.empty()) { + threads.front().join(); + threads.pop(); + } + // Choose the variation that generates the least code, weighted towards header + // length + for (auto& r : results) { + size_t l = 5 * r->header.length() + r->source.length(); + if (l < best_len) { + best_len = l; + best = std::move(*r); + } + } + WriteFile("src/core/ext/transport/chttp2/transport/decode_huff.h", + best.header); + WriteFile("src/core/ext/transport/chttp2/transport/decode_huff.cc", + best.source); + return 0; +} diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 8b907300f42..c7883774c65 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1233,6 +1233,8 @@ src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ src/core/ext/transport/chttp2/transport/chttp2_transport.h \ src/core/ext/transport/chttp2/transport/context_list.cc \ src/core/ext/transport/chttp2/transport/context_list.h \ +src/core/ext/transport/chttp2/transport/decode_huff.cc \ +src/core/ext/transport/chttp2/transport/decode_huff.h \ src/core/ext/transport/chttp2/transport/flow_control.cc \ src/core/ext/transport/chttp2/transport/flow_control.h \ src/core/ext/transport/chttp2/transport/frame.h \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 4e4de516c8c..50fa2db61a9 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1021,6 +1021,8 @@ src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ src/core/ext/transport/chttp2/transport/chttp2_transport.h \ src/core/ext/transport/chttp2/transport/context_list.cc \ src/core/ext/transport/chttp2/transport/context_list.h \ +src/core/ext/transport/chttp2/transport/decode_huff.cc \ +src/core/ext/transport/chttp2/transport/decode_huff.h \ src/core/ext/transport/chttp2/transport/flow_control.cc \ src/core/ext/transport/chttp2/transport/flow_control.h \ src/core/ext/transport/chttp2/transport/frame.h \