[chttp2] Improve huffman decoder bloat (#31633)

* fix hashing

* detect identity

* detect constant, offset

* special case length=2

* composites

* cmt

* more factorizations

* deepen recursion, add cost function

* tweak
pull/31754/head
Craig Tiller 2 years ago committed by GitHub
parent b9d566fac5
commit 14fc11b072
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      src/core/ext/transport/chttp2/transport/decode_huff.cc
  2. 117
      src/core/ext/transport/chttp2/transport/decode_huff.h
  3. 271
      tools/codegen/core/gen_huffman_decompressor.cc

@ -100,13 +100,6 @@ const uint8_t HuffDecoderCommon::table1_0_outer_[256] = {
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};
@ -158,11 +151,8 @@ 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] = {
@ -231,35 +221,16 @@ const uint8_t HuffDecoderCommon::table12_0_outer_[256] = {
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,
@ -269,11 +240,6 @@ const uint16_t HuffDecoderCommon::table26_0_inner_[31] = {
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,
@ -282,6 +248,4 @@ 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

@ -47,22 +47,24 @@ class HuffDecoderCommon {
return table1_0_emit_[emit];
}
static inline uint64_t GetOp5(size_t i) {
return table5_0_inner_[table5_0_outer_[i]];
return (i < 2 ? (i ? 6 : 2) : ((i - 2) ? 14 : 10));
}
static inline uint64_t GetEmit5(size_t, size_t emit) {
return table5_0_emit_[emit];
return (emit < 2 ? (emit + 33) : ((emit - 2) + 40));
}
static inline uint64_t GetOp7(size_t i) {
return table7_0_inner_[table7_0_outer_[i]];
return ((i < 2 ? (i) : ((i - 2) + 1)) < 1
? (((void)(i < 2 ? (i) : ((i - 2) + 1)), 2))
: (((i < 2 ? (i) : ((i - 2) + 1)) - 1) ? 0 : 1));
}
static inline uint64_t GetEmit7(size_t, size_t emit) {
return table7_0_emit_[emit];
return ((void)emit, 63);
}
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];
return (emit < 2 ? (emit ? 39 : 63) : ((emit - 2) ? 124 : 43));
}
static inline uint64_t GetOp9(size_t i) {
return table9_0_inner_[table9_0_outer_[i]];
@ -85,10 +87,10 @@ class HuffDecoderCommon {
return table6_emit_[i >> 6][emit];
}
static inline uint64_t GetOp13(size_t i) {
return table13_0_inner_[table13_0_outer_[i]];
return table13_0_inner_[(i < 3 ? (i) : ((i - 3) / 12 + 3))];
}
static inline uint64_t GetEmit13(size_t, size_t emit) {
return table13_0_emit_[emit];
return (emit < 1 ? (((void)emit, 92)) : ((emit - 1) ? 208 : 195));
}
static inline uint64_t GetOp14(size_t i) { return table14_0_ops_[i]; }
static inline uint64_t GetEmit14(size_t, size_t emit) {
@ -108,86 +110,67 @@ class HuffDecoderCommon {
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 GetOp17(size_t i) { return i ? 3 : 1; }
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]];
return emit ? 142 : 9;
}
static inline uint64_t GetOp18(size_t i) { return i ? 3 : 1; }
static inline uint64_t GetEmit18(size_t, size_t emit) { return emit + 144; }
static inline uint64_t GetOp19(size_t i) { return i ? 3 : 1; }
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]];
return emit ? 159 : 148;
}
static inline uint64_t GetOp20(size_t i) { return i ? 3 : 1; }
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]];
return emit ? 206 : 171;
}
static inline uint64_t GetOp21(size_t i) { return i ? 3 : 1; }
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];
return emit ? 225 : 215;
}
static inline uint64_t GetOp22(size_t i) { return i ? 3 : 1; }
static inline uint64_t GetEmit22(size_t, size_t emit) { return emit + 236; }
static inline uint64_t GetOp23(size_t i) {
return table5_0_inner_[table23_0_outer_[i]];
return (i < 2 ? (i ? 6 : 2) : ((i - 2) ? 14 : 10));
}
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]];
return (emit < 2 ? (emit ? 207 : 199) : ((emit - 2) + 234));
}
static inline uint64_t GetOp24(size_t i) { return table24_0_inner_[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 GetOp25(size_t i) { return table25_0_inner_[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]];
return (
(i < 1 ? (((void)i, 0)) : ((i - 1) / 14 + 1)) < 1
? (((void)(i < 1 ? (((void)i, 0)) : ((i - 1) / 14 + 1)), 2))
: (((i < 1 ? (((void)i, 0)) : ((i - 1) / 14 + 1)) - 1) ? 0 : 1));
}
static inline uint64_t GetEmit27(size_t, size_t emit) {
return table27_0_emit_[emit];
return ((void)emit, 254);
}
static inline uint64_t GetOp26(size_t i) {
return table26_0_inner_[table26_0_outer_[i]];
return table26_0_inner_[(i < 1 ? (((void)i, 0)) : ((i - 1)))];
}
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]];
return (i < 2 ? (i ? 10 : 2) : ((i - 2) ? 6 : 18));
}
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]];
return (emit < 1 ? (((void)emit, 10)) : ((emit - 1) ? 22 : 13));
}
static inline uint64_t GetOp30(size_t i) { return table30_0_inner_[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]];
return table29_0_inner_[(i < 13 ? (i / 2 + 0) : ((i - 13) + 6))];
}
static inline uint64_t GetEmit29(size_t, size_t emit) {
return table29_0_emit_[emit];
@ -207,13 +190,6 @@ class HuffDecoderCommon {
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];
@ -229,9 +205,7 @@ class HuffDecoderCommon {
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];
@ -241,37 +215,16 @@ class HuffDecoderCommon {
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 <typename F>
class HuffDecoder : public HuffDecoderCommon {

@ -17,6 +17,7 @@
#include <fstream>
#include <limits>
#include <map>
#include <memory>
#include <numeric>
#include <queue>
#include <set>
@ -46,14 +47,21 @@ struct Hash {
bool operator<(const Hash& other) const {
return memcmp(bytes, other.bytes, SHA256_DIGEST_LENGTH) < 0;
}
std::string ToString() const {
std::string result;
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
absl::StrAppend(&result, absl::Hex(bytes[i], absl::kZeroPad2));
}
return result;
}
};
// Given a vector of ints (T), return a Hash object with the sha256
template <typename T>
Hash HashVec(const std::vector<T>& v) {
Hash h;
SHA1(reinterpret_cast<const uint8_t*>(v.data()), v.size() * sizeof(T),
h.bytes);
SHA256(reinterpret_cast<const uint8_t*>(v.data()), v.size() * sizeof(T),
h.bytes);
return h;
}
@ -679,27 +687,29 @@ class TableBuilder {
std::vector<std::string> lines;
const uint64_t max_inner = MaxInner();
const uint64_t max_outer = MaxOuter();
std::vector<std::string> emit_names;
std::vector<std::string> inner_names;
std::vector<std::string> outer_names;
std::vector<std::unique_ptr<Array>> emit_names;
std::vector<std::unique_ptr<Array>> inner_names;
std::vector<std::unique_ptr<Array>> 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));
slice_bits != 0, 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));
slice_bits != 0, 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));
slice_bits != 0, 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 GetOp", id, "(size_t i) { return ",
inner_names[0]->Index(outer_names[0]->Index("i")), "; }"));
global_fns->Add(absl::StrCat("static inline uint64_t GetEmit", id,
"(size_t, size_t emit) { return ",
emit_names[0], "[emit]; }"));
emit_names[0]->Index("emit"), "; }"));
} else {
GenCompound(id, emit_names, "emit", "uint8_t", global_decls,
global_values);
@ -765,23 +775,24 @@ class TableBuilder {
Sink* const global_values = builder->ctx_->global_values();
uint64_t max_op = MaxOp();
const int id = builder->id_;
std::vector<std::string> emit_names;
std::vector<std::string> ops_names;
std::vector<std::unique_ptr<Array>> emit_names;
std::vector<std::unique_ptr<Array>> 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));
slice_bits != 0, 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));
slice_bits != 0, 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]; }"));
"(size_t i) { return ",
ops_names[0]->Index("i"), "; }"));
global_fns->Add(absl::StrCat("static inline uint64_t GetEmit", id,
"(size_t, size_t emit) { return ",
emit_names[0], "[emit]; }"));
emit_names[0]->Index("emit"), "; }"));
} else {
GenCompound(id, emit_names, "emit", "uint8_t", global_decls,
global_values);
@ -838,29 +849,221 @@ class TableBuilder {
return table;
}
class Array {
public:
virtual ~Array() = default;
virtual std::string Index(absl::string_view value) = 0;
virtual std::string ArrayName() = 0;
virtual int Cost() = 0;
};
class NamedArray : public Array {
public:
explicit NamedArray(std::string name) : name_(std::move(name)) {}
std::string Index(absl::string_view value) override {
return absl::StrCat(name_, "[", value, "]");
}
std::string ArrayName() override { return name_; }
int Cost() override { abort(); }
private:
std::string name_;
};
class IdentityArray : public Array {
public:
std::string Index(absl::string_view value) override {
return std::string(value);
}
std::string ArrayName() override { abort(); }
int Cost() override { return 0; }
};
class ConstantArray : public Array {
public:
explicit ConstantArray(std::string value) : value_(std::move(value)) {}
std::string Index(absl::string_view index) override {
return absl::StrCat("((void)", index, ", ", value_, ")");
}
std::string ArrayName() override { abort(); }
int Cost() override { return 0; }
private:
std::string value_;
};
class OffsetArray : public Array {
public:
explicit OffsetArray(int offset) : offset_(offset) {}
std::string Index(absl::string_view value) override {
return absl::StrCat(value, " + ", offset_);
}
std::string ArrayName() override { abort(); }
int Cost() override { return 10; }
private:
int offset_;
};
class LinearDivideArray : public Array {
public:
LinearDivideArray(int offset, int divisor)
: offset_(offset), divisor_(divisor) {}
std::string Index(absl::string_view value) override {
return absl::StrCat(value, "/", divisor_, " + ", offset_);
}
std::string ArrayName() override { abort(); }
int Cost() override { return 20 + (offset_ != 0 ? 10 : 0); }
private:
int offset_;
int divisor_;
};
class TwoElemArray : public Array {
public:
TwoElemArray(std::string value0, std::string value1)
: value0_(std::move(value0)), value1_(std::move(value1)) {}
std::string Index(absl::string_view value) override {
return absl::StrCat(value, " ? ", value1_, " : ", value0_);
}
std::string ArrayName() override { abort(); }
int Cost() override { return 40; }
private:
std::string value0_;
std::string value1_;
};
class Composite2Array : public Array {
public:
Composite2Array(std::unique_ptr<Array> a, std::unique_ptr<Array> b,
int split)
: a_(std::move(a)), b_(std::move(b)), split_(split) {}
std::string Index(absl::string_view value) override {
return absl::StrCat(
"(", value, " < ", split_, " ? (", a_->Index(value), ") : (",
b_->Index(absl::StrCat("(", value, "-", split_, ")")), "))");
}
std::string ArrayName() override { abort(); }
int Cost() override { return 40 + a_->Cost() + b_->Cost(); }
private:
std::unique_ptr<Array> a_;
std::unique_ptr<Array> b_;
int split_;
};
// Helper to generate a compound table (an array of arrays)
static void GenCompound(int id, std::vector<std::string> names,
static void GenCompound(int id,
const std::vector<std::unique_ptr<Array>>& arrays,
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(), "];"));
"_", ext, "_[", arrays.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, ","));
ext, "_[", arrays.size(), "] = {"));
for (const std::unique_ptr<Array>& array : arrays) {
global_values->Add(absl::StrCat(" ", array->ArrayName(), ","));
}
global_values->Add("};");
}
// Try to create a simple function equivalent to a mapping implied by a set of
// values.
static const int kMaxArrayToFunctionRecursions = 1;
template <typename T>
static std::unique_ptr<Array> ArrayToFunction(
const std::vector<T>& values,
int recurse = kMaxArrayToFunctionRecursions) {
std::unique_ptr<Array> best = nullptr;
auto note_solution = [&best](std::unique_ptr<Array> a) {
if (best != nullptr && best->Cost() <= a->Cost()) return;
best = std::move(a);
};
// constant => k,k,k,k,...
bool is_constant = true;
for (size_t i = 1; i < values.size(); i++) {
if (values[i] != values[0]) {
is_constant = false;
break;
}
}
if (is_constant) {
note_solution(std::make_unique<ConstantArray>(absl::StrCat(values[0])));
}
// identity => 0,1,2,3,...
bool is_identity = true;
for (size_t i = 0; i < values.size(); i++) {
if (values[i] != i) {
is_identity = false;
break;
}
}
if (is_identity) {
note_solution(std::make_unique<IdentityArray>());
}
// offset => k,k+1,k+2,k+3,...
bool is_offset = true;
for (size_t i = 1; i < values.size(); i++) {
if (values[i] - values[0] != i) {
is_offset = false;
break;
}
}
if (is_offset) {
note_solution(std::make_unique<OffsetArray>(values[0]));
}
// offset => k,k,k+1,k+1,...
for (int d = 2; d < 32; d++) {
bool is_linear = true;
for (size_t i = 1; i < values.size(); i++) {
if (values[i] - values[0] != (i / d)) {
is_linear = false;
break;
}
}
if (is_linear) {
note_solution(std::make_unique<LinearDivideArray>(values[0], d));
}
}
// Two items can be resolved with a conditional
if (values.size() == 2) {
note_solution(std::make_unique<TwoElemArray>(absl::StrCat(values[0]),
absl::StrCat(values[1])));
}
if ((recurse > 0 && values.size() >= 6) ||
(recurse == kMaxArrayToFunctionRecursions)) {
for (size_t i = 1; i < values.size() - 1; i++) {
std::vector<T> left(values.begin(), values.begin() + i);
std::vector<T> right(values.begin() + i, values.end());
std::unique_ptr<Array> left_array = ArrayToFunction(left, recurse - 1);
std::unique_ptr<Array> right_array =
ArrayToFunction(right, recurse - 1);
if (left_array && right_array) {
note_solution(std::make_unique<Composite2Array>(
std::move(left_array), std::move(right_array), i));
}
}
}
return best;
}
// Helper to generate an array of values
template <typename T>
std::string GenArray(std::string name, std::string type,
const std::vector<T>& values, bool hex,
Sink* global_decls, Sink* global_values) const {
std::unique_ptr<Array> GenArray(bool force_array, std::string name,
std::string type,
const std::vector<T>& values, bool hex,
Sink* global_decls,
Sink* global_values) const {
if (!force_array) {
auto fn = ArrayToFunction(values);
if (fn != nullptr) return fn;
}
auto previous_name = ctx_->PreviousNameForArtifact(name, HashVec(values));
if (previous_name.has_value()) {
return absl::StrCat(*previous_name, "_");
return std::make_unique<NamedArray>(absl::StrCat(*previous_name, "_"));
}
std::vector<std::string> elems;
elems.reserve(values.size());
@ -884,7 +1087,7 @@ class TableBuilder {
name, "_[", values.size(), "] = {"));
global_values->Add(absl::StrCat(" ", data));
global_values->Add("};");
return absl::StrCat(name, "_");
return std::make_unique<NamedArray>(absl::StrCat(name, "_"));
}
// Choose an encoding for this set of tables.

Loading…
Cancel
Save