diff options
| -rw-r--r-- | compiler/debug/elf_debug_info_writer.h | 13 | ||||
| -rw-r--r-- | dexdump/dexdump.cc | 24 | ||||
| -rw-r--r-- | dexlayout/dexlayout.cc | 25 | ||||
| -rw-r--r-- | libdexfile/dex/code_item_accessors-inl.h | 13 | ||||
| -rw-r--r-- | libdexfile/dex/code_item_accessors.h | 5 | ||||
| -rw-r--r-- | libdexfile/dex/dex_file-inl.h | 19 | ||||
| -rw-r--r-- | libdexfile/dex/dex_file.h | 10 | ||||
| -rw-r--r-- | libdexfile/dex/dex_file_loader_test.cc | 6 | ||||
| -rw-r--r-- | openjdkjvmti/ti_method.cc | 157 | ||||
| -rw-r--r-- | runtime/debugger.cc | 74 | ||||
| -rw-r--r-- | tools/dexanalyze/dexanalyze_strings.cc | 22 |
11 files changed, 153 insertions, 215 deletions
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 233f61cec3..fe05992960 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -41,12 +41,6 @@ namespace art { namespace debug { -typedef std::vector<DexFile::LocalInfo> LocalInfos; - -static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { - static_cast<LocalInfos*>(ctx)->push_back(entry); -} - static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) { std::vector<const char*> names; DCHECK(mi->dex_file != nullptr); @@ -251,11 +245,12 @@ class ElfCompilationUnitWriter { } // Write local variables. - LocalInfos local_infos; + std::vector<DexFile::LocalInfo> local_infos; if (accessor.DecodeDebugLocalInfo(is_static, mi->dex_method_index, - LocalInfoCallback, - &local_infos)) { + [&](const DexFile::LocalInfo& entry) { + local_infos.push_back(entry); + })) { for (const DexFile::LocalInfo& var : local_infos) { if (var.reg_ < accessor.RegistersSize() - accessor.InsSize()) { info_.StartTag(DW_TAG_variable); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 31bc6e39e8..95d88be27d 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -751,16 +751,6 @@ static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) } /* - * Callback for dumping locals table entry. - */ -static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) { - const char* signature = entry.signature_ != nullptr ? entry.signature_ : ""; - fprintf(gOutFile, " 0x%04x - 0x%04x reg=%d %s %s %s\n", - entry.start_address_, entry.end_address_, entry.reg_, - entry.name_, entry.descriptor_, signature); -} - -/* * Helper for dumpInstruction(), which builds the string * representation for the index in the given instruction. * Returns a pointer to a buffer of sufficient size. @@ -1198,7 +1188,19 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, return false; }); fprintf(gOutFile, " locals : \n"); - accessor.DecodeDebugLocalInfo(is_static, idx, dumpLocalsCb, nullptr); + accessor.DecodeDebugLocalInfo(is_static, + idx, + [&](const DexFile::LocalInfo& entry) { + const char* signature = entry.signature_ != nullptr ? entry.signature_ : ""; + fprintf(gOutFile, + " 0x%04x - 0x%04x reg=%d %s %s %s\n", + entry.start_address_, + entry.end_address_, + entry.reg_, + entry.name_, + entry.descriptor_, + signature); + }); } /* diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 148c0c7dd6..b539f5d130 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -1037,17 +1037,6 @@ void DexLayout::DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32 } /* - * Callback for dumping locals table entry. - */ -static void DumpLocalsCb(void* context, const DexFile::LocalInfo& entry) { - const char* signature = entry.signature_ != nullptr ? entry.signature_ : ""; - FILE* out_file = reinterpret_cast<FILE*>(context); - fprintf(out_file, " 0x%04x - 0x%04x reg=%d %s %s %s\n", - entry.start_address_, entry.end_address_, entry.reg_, - entry.name_, entry.descriptor_, signature); -} - -/* * Lookup functions. */ static const char* StringDataByIdx(uint32_t idx, dex_ir::Header* header) { @@ -1140,8 +1129,18 @@ void DexLayout::DumpCode(uint32_t idx, StringDataByTypeIdx(dchecked_integral_cast<uint16_t>(idx), this->header_); }, - DumpLocalsCb, - out_file_); + [&](const DexFile::LocalInfo& entry) { + const char* signature = + entry.signature_ != nullptr ? entry.signature_ : ""; + fprintf(out_file_, + " 0x%04x - 0x%04x reg=%d %s %s %s\n", + entry.start_address_, + entry.end_address_, + entry.reg_, + entry.name_, + entry.descriptor_, + signature); + }); } } diff --git a/libdexfile/dex/code_item_accessors-inl.h b/libdexfile/dex/code_item_accessors-inl.h index c7e876e16a..bbf2224b06 100644 --- a/libdexfile/dex/code_item_accessors-inl.h +++ b/libdexfile/dex/code_item_accessors-inl.h @@ -184,19 +184,18 @@ inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& cod CodeItemDataAccessor::Init(code_item); } -template<typename NewLocalCallback> -inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static, - uint32_t method_idx, - NewLocalCallback new_local, - void* context) const { +template<typename NewLocalVisitor> +inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo( + bool is_static, + uint32_t method_idx, + const NewLocalVisitor& new_local) const { return dex_file_->DecodeDebugLocalInfo(RegistersSize(), InsSize(), InsnsSizeInCodeUnits(), DebugInfoOffset(), is_static, method_idx, - new_local, - context); + new_local); } template <typename Visitor> diff --git a/libdexfile/dex/code_item_accessors.h b/libdexfile/dex/code_item_accessors.h index c2aa23c17d..c307c9f70b 100644 --- a/libdexfile/dex/code_item_accessors.h +++ b/libdexfile/dex/code_item_accessors.h @@ -151,11 +151,10 @@ class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { return debug_info_offset_; } - template<typename NewLocalCallback> + template<typename NewLocalVisitor> bool DecodeDebugLocalInfo(bool is_static, uint32_t method_idx, - NewLocalCallback new_local, - void* context) const; + const NewLocalVisitor& new_local) const; // Visit each parameter in the debug information. Returns the line number. // The argument of the Visitor is dex::StringIndex. diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index eca9ee945b..eae7efc2bc 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -215,10 +215,9 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, uint16_t registers_size, uint16_t ins_size, uint16_t insns_size_in_code_units, - IndexToStringData index_to_string_data, - TypeIndexToStringData type_index_to_string_data, - NewLocalCallback new_local_callback, - void* context) { + const IndexToStringData& index_to_string_data, + const TypeIndexToStringData& type_index_to_string_data, + const NewLocalCallback& new_local_callback) { if (stream == nullptr) { return false; } @@ -278,7 +277,7 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, for (uint16_t reg = 0; reg < registers_size; reg++) { if (local_in_reg[reg].is_live_) { local_in_reg[reg].end_address_ = insns_size_in_code_units; - new_local_callback(context, local_in_reg[reg]); + new_local_callback(local_in_reg[reg]); } } return true; @@ -307,7 +306,7 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, // Emit what was previously there, if anything if (local_in_reg[reg].is_live_) { local_in_reg[reg].end_address_ = address; - new_local_callback(context, local_in_reg[reg]); + new_local_callback(local_in_reg[reg]); } local_in_reg[reg].name_ = index_to_string_data(name_idx); @@ -329,7 +328,7 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, // closed register is sloppy, but harmless if no further action is taken. if (local_in_reg[reg].is_live_) { local_in_reg[reg].end_address_ = address; - new_local_callback(context, local_in_reg[reg]); + new_local_callback(local_in_reg[reg]); local_in_reg[reg].is_live_ = false; } break; @@ -369,8 +368,7 @@ bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size, uint32_t debug_info_offset, bool is_static, uint32_t method_idx, - NewLocalCallback new_local_callback, - void* context) const { + const NewLocalCallback& new_local_callback) const { const uint8_t* const stream = GetDebugInfoStream(debug_info_offset); if (stream == nullptr) { return false; @@ -396,8 +394,7 @@ bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size, return StringByTypeIdx(dex::TypeIndex( dchecked_integral_cast<uint16_t>(idx))); }, - new_local_callback, - context); + new_local_callback); } template<typename DexDebugNewPosition, typename IndexToStringData> diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 3e4a4811c3..30d8b6d9bf 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -874,10 +874,9 @@ class DexFile { uint16_t registers_size, uint16_t ins_size, uint16_t insns_size_in_code_units, - IndexToStringData index_to_string_data, - TypeIndexToStringData type_index_to_string_data, - NewLocalCallback new_local, - void* context); + const IndexToStringData& index_to_string_data, + const TypeIndexToStringData& type_index_to_string_data, + const NewLocalCallback& new_local) NO_THREAD_SAFETY_ANALYSIS; template<typename NewLocalCallback> bool DecodeDebugLocalInfo(uint32_t registers_size, uint32_t ins_size, @@ -885,8 +884,7 @@ class DexFile { uint32_t debug_info_offset, bool is_static, uint32_t method_idx, - NewLocalCallback new_local, - void* context) const; + const NewLocalCallback& new_local) const; // Returns false if there is no debugging information or if it cannot be decoded. template<typename DexDebugNewPosition, typename IndexToStringData> diff --git a/libdexfile/dex/dex_file_loader_test.cc b/libdexfile/dex/dex_file_loader_test.cc index 5bb01dd7ac..53786171cc 100644 --- a/libdexfile/dex/dex_file_loader_test.cc +++ b/libdexfile/dex/dex_file_loader_test.cc @@ -480,10 +480,6 @@ TEST_F(DexFileLoaderTest, GetStringWithNoIndex) { EXPECT_EQ(raw->StringByTypeIdx(idx), nullptr); } -static void Callback(void* context ATTRIBUTE_UNUSED, - const DexFile::LocalInfo& entry ATTRIBUTE_UNUSED) { -} - TEST_F(DexFileLoaderTest, OpenDexDebugInfoLocalNullType) { std::vector<uint8_t> dex_bytes; std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64(kRawDexDebugInfoLocalNullType, @@ -496,7 +492,7 @@ TEST_F(DexFileLoaderTest, OpenDexDebugInfoLocalNullType) { const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, kMethodIdx)); CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx); - ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr)); + ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, VoidFunctor())); } } // namespace art diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index 742a9fa0a3..295894157c 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -206,76 +206,59 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, return ERR(ABSENT_INFORMATION); } - struct LocalVariableContext { - explicit LocalVariableContext(jvmtiEnv* jenv) : env_(jenv), variables_(), err_(OK) {} - - static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) { - reinterpret_cast<LocalVariableContext*>(raw_ctx)->Insert(entry); - } - - void Insert(const art::DexFile::LocalInfo& entry) { - if (err_ != OK) { - return; - } - JvmtiUniquePtr<char[]> name_str = CopyString(env_, entry.name_, &err_); - if (err_ != OK) { - return; + std::vector<jvmtiLocalVariableEntry> variables; + jvmtiError err = OK; + + auto release = [&](jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) { + jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables.size(); + if (err != OK || + (err = env->Allocate(table_size, + reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) { + for (jvmtiLocalVariableEntry& e : variables) { + env->Deallocate(reinterpret_cast<unsigned char*>(e.name)); + env->Deallocate(reinterpret_cast<unsigned char*>(e.signature)); + env->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature)); } - JvmtiUniquePtr<char[]> sig_str = CopyString(env_, entry.descriptor_, &err_); - if (err_ != OK) { - return; - } - JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env_, entry.signature_, &err_); - if (err_ != OK) { - return; - } - variables_.push_back({ - .start_location = static_cast<jlocation>(entry.start_address_), - .length = static_cast<jint>(entry.end_address_ - entry.start_address_), - .name = name_str.release(), - .signature = sig_str.release(), - .generic_signature = generic_sig_str.release(), - .slot = entry.reg_, - }); + return err; } + *out_entry_count_ptr = variables.size(); + memcpy(*out_table_ptr, variables.data(), table_size); + return OK; + }; - jvmtiError Release(jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) { - jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables_.size(); - if (err_ != OK || - (err_ = env_->Allocate(table_size, - reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) { - Cleanup(); - return err_; - } else { - *out_entry_count_ptr = variables_.size(); - memcpy(*out_table_ptr, variables_.data(), table_size); - return OK; - } + auto visitor = [&](const art::DexFile::LocalInfo& entry) { + if (err != OK) { + return; } - - void Cleanup() { - for (jvmtiLocalVariableEntry& e : variables_) { - env_->Deallocate(reinterpret_cast<unsigned char*>(e.name)); - env_->Deallocate(reinterpret_cast<unsigned char*>(e.signature)); - env_->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature)); - } + JvmtiUniquePtr<char[]> name_str = CopyString(env, entry.name_, &err); + if (err != OK) { + return; } - - jvmtiEnv* env_; - std::vector<jvmtiLocalVariableEntry> variables_; - jvmtiError err_; + JvmtiUniquePtr<char[]> sig_str = CopyString(env, entry.descriptor_, &err); + if (err != OK) { + return; + } + JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env, entry.signature_, &err); + if (err != OK) { + return; + } + variables.push_back({ + .start_location = static_cast<jlocation>(entry.start_address_), + .length = static_cast<jint>(entry.end_address_ - entry.start_address_), + .name = name_str.release(), + .signature = sig_str.release(), + .generic_signature = generic_sig_str.release(), + .slot = entry.reg_, + }); }; - LocalVariableContext context(env); if (!accessor.DecodeDebugLocalInfo(art_method->IsStatic(), art_method->GetDexMethodIndex(), - LocalVariableContext::Callback, - &context)) { + visitor)) { // Something went wrong with decoding the debug information. It might as well not be there. return ERR(ABSENT_INFORMATION); - } else { - return context.Release(entry_count_ptr, table_ptr); } + return release(entry_count_ptr, table_ptr); } jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED, @@ -614,55 +597,25 @@ class CommonLocalVariableClosure : public art::Closure { if (!accessor.HasCodeItem()) { return ERR(OPAQUE_FRAME); } - - struct GetLocalVariableInfoContext { - explicit GetLocalVariableInfoContext(jint slot, - uint32_t pc, - std::string* out_descriptor, - art::Primitive::Type* out_type) - : found_(false), jslot_(slot), pc_(pc), descriptor_(out_descriptor), type_(out_type) { - *descriptor_ = ""; - *type_ = art::Primitive::kPrimVoid; + bool found = false; + *type = art::Primitive::kPrimVoid; + descriptor->clear(); + auto visitor = [&](const art::DexFile::LocalInfo& entry) { + if (!found && + entry.start_address_ <= dex_pc && + entry.end_address_ > dex_pc && + entry.reg_ == slot_) { + found = true; + *type = art::Primitive::GetType(entry.descriptor_[0]); + *descriptor = entry.descriptor_; } - - static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) { - reinterpret_cast<GetLocalVariableInfoContext*>(raw_ctx)->Handle(entry); - } - - void Handle(const art::DexFile::LocalInfo& entry) { - if (found_) { - return; - } else if (entry.start_address_ <= pc_ && - entry.end_address_ > pc_ && - entry.reg_ == jslot_) { - found_ = true; - *type_ = art::Primitive::GetType(entry.descriptor_[0]); - *descriptor_ = entry.descriptor_; - } - return; - } - - bool found_; - jint jslot_; - uint32_t pc_; - std::string* descriptor_; - art::Primitive::Type* type_; }; - - GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type); - if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(), - accessor.InsSize(), - accessor.InsnsSizeInCodeUnits(), - accessor.DebugInfoOffset(), - method->IsStatic(), - method->GetDexMethodIndex(), - GetLocalVariableInfoContext::Callback, - &context) || !context.found_) { + if (!accessor.DecodeDebugLocalInfo(method->IsStatic(), method->GetDexMethodIndex(), visitor) || + !found) { // Something went wrong with decoding the debug information. It might as well not be there. return ERR(INVALID_SLOT); - } else { - return OK; } + return OK; } jvmtiError result_; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 7b4fa6e16b..b108920482 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1700,37 +1700,6 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool with_generic, JDWP::ExpandBuf* pReply) { - struct DebugCallbackContext { - ArtMethod* method; - JDWP::ExpandBuf* pReply; - size_t variable_count; - bool with_generic; - - static void Callback(void* context, const DexFile::LocalInfo& entry) - REQUIRES_SHARED(Locks::mutator_lock_) { - DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context); - - uint16_t slot = entry.reg_; - VLOG(jdwp) << StringPrintf(" %2zd: %d(%d) '%s' '%s' '%s' actual slot=%d mangled slot=%d", - pContext->variable_count, entry.start_address_, - entry.end_address_ - entry.start_address_, - entry.name_, entry.descriptor_, entry.signature_, slot, - MangleSlot(slot, pContext->method)); - - slot = MangleSlot(slot, pContext->method); - - expandBufAdd8BE(pContext->pReply, entry.start_address_); - expandBufAddUtf8String(pContext->pReply, entry.name_); - expandBufAddUtf8String(pContext->pReply, entry.descriptor_); - if (pContext->with_generic) { - expandBufAddUtf8String(pContext->pReply, entry.signature_); - } - expandBufAdd4BE(pContext->pReply, entry.end_address_- entry.start_address_); - expandBufAdd4BE(pContext->pReply, slot); - - ++pContext->variable_count; - } - }; ArtMethod* m = FromMethodId(method_id); CodeItemDebugInfoAccessor accessor(m->DexInstructionDebugInfo()); @@ -1742,24 +1711,39 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi size_t variable_count_offset = expandBufGetLength(pReply); expandBufAdd4BE(pReply, 0); - DebugCallbackContext context; - context.method = m; - context.pReply = pReply; - context.variable_count = 0; - context.with_generic = with_generic; + size_t variable_count = 0; if (accessor.HasCodeItem()) { - m->GetDexFile()->DecodeDebugLocalInfo(accessor.RegistersSize(), - accessor.InsSize(), - accessor.InsnsSizeInCodeUnits(), - accessor.DebugInfoOffset(), - m->IsStatic(), - m->GetDexMethodIndex(), - DebugCallbackContext::Callback, - &context); + accessor.DecodeDebugLocalInfo(m->IsStatic(), + m->GetDexMethodIndex(), + [&](const DexFile::LocalInfo& entry) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint16_t slot = entry.reg_; + VLOG(jdwp) << StringPrintf(" %2zd: %d(%d) '%s' '%s' '%s' actual slot=%d mangled slot=%d", + variable_count, + entry.start_address_, + entry.end_address_ - entry.start_address_, + entry.name_, + entry.descriptor_, entry.signature_, + slot, + MangleSlot(slot, m)); + + slot = MangleSlot(slot, m); + + expandBufAdd8BE(pReply, entry.start_address_); + expandBufAddUtf8String(pReply, entry.name_); + expandBufAddUtf8String(pReply, entry.descriptor_); + if (with_generic) { + expandBufAddUtf8String(pReply, entry.signature_); + } + expandBufAdd4BE(pReply, entry.end_address_- entry.start_address_); + expandBufAdd4BE(pReply, slot); + + ++variable_count; + }); } - JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count); + JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, variable_count); } void Dbg::OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return_value, diff --git a/tools/dexanalyze/dexanalyze_strings.cc b/tools/dexanalyze/dexanalyze_strings.cc index de5c34ecf4..dcadb59e35 100644 --- a/tools/dexanalyze/dexanalyze_strings.cc +++ b/tools/dexanalyze/dexanalyze_strings.cc @@ -119,8 +119,25 @@ class PrefixStrings { return false; } const uint8_t* prefix_data = &dictionary_.prefix_data_[prefix_offset]; - return memcmp(prefix_data, data, prefix_len) == 0u && - memcmp(suffix_data, data + prefix_len, len - prefix_len) == 0u; + if ((true)) { + return memcmp(prefix_data, data, prefix_len) == 0u && + memcmp(suffix_data, data + prefix_len, len - prefix_len) == 0u; + } else { + len -= prefix_len; + while (prefix_len != 0u) { + if (*prefix_data++ != *data++) { + return false; + } + --prefix_len; + } + while (len != 0u) { + if (*suffix_data++ != *data++) { + return false; + } + --len; + } + return true; + } } public: @@ -164,7 +181,6 @@ class NormalStrings { std::vector<uint32_t> string_offsets_; }; - // Node value = (distance from root) * (occurrences - 1). class MatchTrie { public: |