From 8ab45cb43d46b96c55c9e4d19b45a2453557f76a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 10 Feb 2018 21:36:15 -0800 Subject: [PATCH] std::string {hover,comments} -> NTString (null-terminated string) Typically, sizeof(std::string) = 32, sizeof(NTString) = 8 hover and comments are usually empty and thus wasteful to spend 24*2 bytes for them --- src/indexer.cc | 47 ++++++++++++++---- src/indexer.h | 13 ++--- src/language_server_api.h | 2 +- src/messages/cquery_type_hierarchy_tree.cc | 4 +- src/messages/text_document_hover.cc | 21 ++++---- src/ntstring.h | 57 ++++++++++++++++++++++ src/query_utils.cc | 8 +-- src/serializer.cc | 22 ++++----- src/serializer.h | 5 +- 9 files changed, 132 insertions(+), 47 deletions(-) create mode 100644 src/ntstring.h diff --git a/src/indexer.cc b/src/indexer.cc index c030de12..c2f86e95 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -434,6 +434,25 @@ std::string GetDocumentContentInRange(CXTranslationUnit cx_tu, return result; } +void SetUsePreflight(IndexFile* db, ClangCursor lex_parent) { + switch (GetSymbolKind(lex_parent.get_kind())) { + default: + break; + case SymbolKind::Func: { + (void)db->ToFuncId(lex_parent.cx_cursor); + break; + } + case SymbolKind::Type: { + (void)db->ToTypeId(lex_parent.cx_cursor); + break; + } + case SymbolKind::Var: { + (void)db->ToVarId(lex_parent.cx_cursor); + break; + } + } +} + void SetUse(IndexFile* db, Maybe* def, Range range, ClangCursor lex_parent, Role role) { switch (GetSymbolKind(lex_parent.get_kind())) { default: @@ -561,8 +580,8 @@ void SetVarDetail(IndexVar* var, def.detailed_name = param->PrettyPrintCursor(cursor.cx_cursor, false); def.hover = std::move(qualified_name); #else - def.detailed_name = std::move(type_name); - ConcatTypeAndName(def.detailed_name, qualified_name); + ConcatTypeAndName(type_name, qualified_name); + def.detailed_name = type_name; // Append the textual initializer, bit field, constructor to |hover|. // Omit |hover| for these types: // int (*a)(); int (&a)(); int (&&a)(); int a[1]; auto x = ... @@ -580,7 +599,7 @@ void SetVarDetail(IndexVar* var, optional spell_end = fc.ToOffset(cursor.get_spelling_range().end); optional extent_end = fc.ToOffset(cursor.get_extent().end); if (extent_end && *spell_end < *extent_end) - def.hover = def.detailed_name + + def.hover = std::string(def.detailed_name.c_str()) + fc.content.substr(*spell_end, *extent_end - *spell_end); } #endif @@ -1210,21 +1229,22 @@ ClangCursor::VisitResult VisitMacroDefinitionAndExpansions(ClangCursor cursor, else decl_usr = cursor.get_referenced().get_usr_hash(); + SetUsePreflight(db, parent); IndexVar* var_def = db->Resolve(db->ToVarId(decl_usr)); if (cursor.get_kind() == CXCursor_MacroDefinition) { CXSourceRange cx_extent = clang_getCursorExtent(cursor.cx_cursor); var_def->def.detailed_name = cursor.get_display_name(); var_def->def.short_name_offset = 0; - var_def->def.short_name_size = int(var_def->def.detailed_name.size()); + var_def->def.short_name_size = + int16_t(strlen(var_def->def.detailed_name.c_str())); var_def->def.hover = "#define " + GetDocumentContentInRange(param->tu->cx_tu, cx_extent); var_def->def.kind = ClangSymbolKind::Macro; var_def->def.comments = cursor.get_comments(); - ClangCursor lex_parent; - SetUse(db, &var_def->def.spell, decl_loc_spelling, lex_parent, + SetUse(db, &var_def->def.spell, decl_loc_spelling, parent, Role::Definition); SetUse(db, &var_def->def.extent, - ResolveCXSourceRange(cx_extent, nullptr), lex_parent, Role::None); + ResolveCXSourceRange(cx_extent, nullptr), parent, Role::None); } else UniqueAddUse(db, var_def->uses, decl_loc_spelling, parent); @@ -1257,6 +1277,7 @@ ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, case CXCursor_DeclRefExpr: { ClangCursor ref_cursor = clang_getCursorReferenced(cursor.cx_cursor); if (ref_cursor.get_kind() == CXCursor_NonTypeTemplateParameter) { + SetUsePreflight(db, parent); IndexVar* ref_var = db->Resolve(db->ToVarId(ref_cursor.get_usr_hash())); if (ref_var->def.detailed_name.empty()) { @@ -1305,6 +1326,7 @@ ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, case CXCursor_TemplateRef: { ClangCursor ref_cursor = clang_getCursorReferenced(cursor.cx_cursor); if (ref_cursor.get_kind() == CXCursor_TemplateTemplateParameter) { + SetUsePreflight(db, parent); IndexType* ref_index = db->Resolve(db->ToTypeId(ref_cursor.get_usr_hash())); // TODO It seems difficult to get references to template template @@ -1321,7 +1343,8 @@ ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, ref_index->def.detailed_name = ref_cursor.get_spelling(); #endif ref_index->def.short_name_offset = 0; - ref_index->def.short_name_size = ref_index->def.detailed_name.size(); + ref_index->def.short_name_size = + int16_t(strlen(ref_index->def.detailed_name.c_str())); ref_index->def.kind = ClangSymbolKind::Parameter; } UniqueAddUseSpell(db, ref_index->uses, cursor); @@ -1331,6 +1354,7 @@ ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, case CXCursor_TypeRef: { ClangCursor ref_cursor = clang_getCursorReferenced(cursor.cx_cursor); if (ref_cursor.get_kind() == CXCursor_TemplateTypeParameter) { + SetUsePreflight(db, parent); IndexType* ref_index = db->Resolve(db->ToTypeId(ref_cursor.get_usr_hash())); // TODO It seems difficult to get a FunctionTemplate's template @@ -1347,7 +1371,8 @@ ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, ref_index->def.detailed_name = ref_cursor.get_spelling(); #endif ref_index->def.short_name_offset = 0; - ref_index->def.short_name_size = ref_index->def.detailed_name.size(); + ref_index->def.short_name_size = + int16_t(strlen(ref_index->def.detailed_name.c_str())); ref_index->def.kind = ClangSymbolKind::Parameter; } UniqueAddUseSpell(db, ref_index->uses, cursor); @@ -1455,6 +1480,7 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { NamespaceHelper* ns = ¶m->ns; ClangCursor lex_parent(fromContainer(decl->lexicalContainer)); + SetUsePreflight(db, lex_parent); switch (decl->entityInfo->kind) { case CXIdxEntity_CXXNamespace: { @@ -1745,7 +1771,7 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { if (extent_start && spell_start && spell_end && extent_end) { type->def.hover = fc.content.substr(*extent_start, *spell_start - *extent_start) + - type->def.detailed_name + + type->def.detailed_name.c_str() + fc.content.substr(*spell_end, *extent_end - *spell_end); } } @@ -1935,6 +1961,7 @@ void OnIndexReference(CXClientData client_data, const CXIdxEntityRefInfo* ref) { ClangCursor cursor(ref->cursor); ClangCursor lex_parent(fromContainer(ref->container)); + SetUsePreflight(db, lex_parent); switch (ref->referencedEntity->kind) { case CXIdxEntity_CXXNamespaceAlias: diff --git a/src/indexer.h b/src/indexer.h index 20a25427..4d61cd86 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -9,6 +9,7 @@ #include "file_contents.h" #include "language_server_api.h" #include "maybe.h" +#include "ntstring.h" #include "performance.h" #include "position.h" #include "serializer.h" @@ -160,8 +161,8 @@ template struct TypeDefDefinitionData { // General metadata. std::string detailed_name; - std::string hover; - std::string comments; + NTString hover; + NTString comments; // While a class/type can technically have a separate declaration/definition, // it doesn't really happen in practice. The declaration never contains @@ -258,8 +259,8 @@ template struct FuncDefDefinitionData { // General metadata. std::string detailed_name; - std::string hover; - std::string comments; + NTString hover; + NTString comments; Maybe spell; Maybe extent; @@ -368,8 +369,8 @@ template struct VarDefDefinitionData { // General metadata. std::string detailed_name; - std::string hover; - std::string comments; + NTString hover; + NTString comments; // TODO: definitions should be a list of ranges, since there can be more // than one - when?? Maybe spell; diff --git a/src/language_server_api.h b/src/language_server_api.h index 4b635268..bb730b1f 100644 --- a/src/language_server_api.h +++ b/src/language_server_api.h @@ -180,7 +180,7 @@ MAKE_REFLECT_STRUCT(lsLocation, uri, range); // cquery extension struct lsLocationEx : lsLocation { - optional containerName; + optional containerName; }; MAKE_REFLECT_STRUCT(lsLocationEx, uri, range, containerName); diff --git a/src/messages/cquery_type_hierarchy_tree.cc b/src/messages/cquery_type_hierarchy_tree.cc index f2091feb..b94c1eea 100644 --- a/src/messages/cquery_type_hierarchy_tree.cc +++ b/src/messages/cquery_type_hierarchy_tree.cc @@ -14,7 +14,7 @@ REGISTER_IPC_MESSAGE(Ipc_CqueryTypeHierarchyTree); struct Out_CqueryTypeHierarchyTree : public lsOutMessage { struct TypeEntry { - std::string name; + std::string_view name; optional location; std::vector children; }; @@ -36,7 +36,7 @@ BuildParentInheritanceHierarchyForType(QueryDatabase* db, EachWithGen(db->types, root_type.def->parents, [&](QueryType& parent_type) { Out_CqueryTypeHierarchyTree::TypeEntry parent_entry; - parent_entry.name = parent_type.def->detailed_name; + parent_entry.name = parent_type.def->detailed_name.c_str(); if (parent_type.def->spell) parent_entry.location = GetLsLocation( db, working_files, *parent_type.def->spell); diff --git a/src/messages/text_document_hover.cc b/src/messages/text_document_hover.cc index 1875d958..45c4a949 100644 --- a/src/messages/text_document_hover.cc +++ b/src/messages/text_document_hover.cc @@ -11,25 +11,28 @@ std::pair GetCommentsAndHover( case SymbolKind::Type: { QueryType& type = db->types[sym.Idx()]; if (type.def) - return {type.def->comments, type.def->hover.size() - ? type.def->hover - : type.def->detailed_name}; + return {type.def->comments, + !type.def->hover.empty() + ? std::string_view(type.def->hover) + : std::string_view(type.def->detailed_name)}; break; } case SymbolKind::Func: { QueryFunc& func = db->funcs[sym.Idx()]; if (func.def) - return {func.def->comments, func.def->hover.size() - ? func.def->hover - : func.def->detailed_name}; + return {func.def->comments, + !func.def->hover.empty() + ? std::string_view(func.def->hover) + : std::string_view(func.def->detailed_name)}; break; } case SymbolKind::Var: { QueryVar& var = db->vars[sym.Idx()]; if (var.def) - return {var.def->comments, var.def->hover.size() - ? var.def->hover - : var.def->detailed_name}; + return {var.def->comments, + !var.def->hover.empty() + ? std::string_view(var.def->hover) + : std::string_view(var.def->detailed_name)}; break; } case SymbolKind::File: diff --git a/src/ntstring.h b/src/ntstring.h new file mode 100644 index 00000000..3aa16687 --- /dev/null +++ b/src/ntstring.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include +#include +#include + +class Reader; +class Writer; + +// Null-terminated string +// This is used in Query{Func,Type,Var}::def to reduce memory footprint. +class NTString { + using size_type = std::string::size_type; + std::unique_ptr str; + + public: + NTString() : str(new char[1]()) {} + NTString(const NTString& o) : str(new char[strlen(o.c_str()) + 1]) { + strcpy(str.get(), o.c_str()); + } + NTString(NTString&& o) = default; + NTString(std::string_view sv) { + *this = sv; + } + + operator std::string_view() const { return str.get(); } + const char* c_str() const { return str.get(); } + std::string_view substr(size_type pos, size_type cnt) const { + return std::string_view(c_str() + pos, cnt); + } + bool empty() const { return !str || str.get()[0] == '\0'; } + size_type find(const char* s) { + const char *p = strstr(c_str(), s); + return p ? std::string::size_type(p - c_str()) : std::string::npos; + } + size_type find(const char* s, size_type pos, size_type cnt) { + auto* p = (const char*)memmem(c_str() + pos, strlen(c_str()) - pos, s, cnt); + return p ? std::string::size_type(p - c_str()) : std::string::npos; + } + size_type find(std::string_view sv) { + return find(sv.data(), 0, sv.size()); + } + + void operator=(std::string_view sv) { + str = std::unique_ptr(new char[sv.size() + 1]); + memcpy(str.get(), sv.data(), sv.size()); + str.get()[sv.size()] = '\0'; + } + void operator=(const NTString& o) { + *this = static_cast(o); + } + bool operator==(const NTString& o) const { + return strcmp(c_str(), o.c_str()) == 0; + } +}; diff --git a/src/query_utils.cc b/src/query_utils.cc index 13f5384a..c169f612 100644 --- a/src/query_utils.cc +++ b/src/query_utils.cc @@ -406,19 +406,19 @@ optional GetLsLocationEx(QueryDatabase* db, case SymbolKind::Func: { QueryFunc& func = db->GetFunc(use); if (func.def) - ret.containerName = func.def->detailed_name; + ret.containerName = std::string_view(func.def->detailed_name); break; } case SymbolKind::Type: { QueryType& type = db->GetType(use); if (type.def) - ret.containerName = type.def->detailed_name; + ret.containerName = std::string_view(type.def->detailed_name); break; } case SymbolKind::Var: { QueryVar& var = db->GetVar(use); if (var.def) - ret.containerName = var.def->detailed_name; + ret.containerName = std::string_view(var.def->detailed_name); break; } } @@ -470,7 +470,7 @@ optional GetSymbolInfo(QueryDatabase* db, info.name = type.def->ShortName(); else info.name = type.def->detailed_name; - if (type.def->detailed_name != type.def->ShortName()) + if (type.def->detailed_name.c_str() != type.def->ShortName()) info.containerName = type.def->detailed_name; // TODO ClangSymbolKind -> lsSymbolKind switch (type.def->kind) { diff --git a/src/serializer.cc b/src/serializer.cc index 3cb45aad..b17f2a0d 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -125,18 +125,14 @@ void Reflect(Writer& visitor, std::string_view& data) { visitor.String(&data[0], (rapidjson::SizeType)data.size()); } -void Reflect(Reader& visitor, std::unique_ptr& value) { +void Reflect(Reader& visitor, NTString& value) { if (!visitor.IsString()) throw std::invalid_argument("std::string"); - std::string t = visitor.GetString(); - value = std::unique_ptr(new char[t.size() + 1]); - strcpy(value.get(), t.c_str()); + value = visitor.GetString(); } -void Reflect(Writer& visitor, std::unique_ptr& value) { - if (!value) - visitor.String(""); - else - visitor.String(value.get()); +void Reflect(Writer& visitor, NTString& value) { + const char* s = value.c_str(); + visitor.String(s ? s : ""); } // TODO: Move this to indexer.cc @@ -169,9 +165,9 @@ void ReflectHoverAndComments(Reader& visitor, Def& def) { template void ReflectHoverAndComments(Writer& visitor, Def& def) { // Don't emit empty hover and comments in JSON test mode. - if (!gTestOutputMode || def.hover.size()) + if (!gTestOutputMode || !def.hover.empty()) ReflectMember(visitor, "hover", def.hover); - if (!gTestOutputMode || def.comments.size()) + if (!gTestOutputMode || !def.comments.empty()) ReflectMember(visitor, "comments", def.comments); } @@ -192,8 +188,8 @@ void ReflectShortName(Reader& visitor, Def& def) { template void ReflectShortName(Writer& visitor, Def& def) { if (gTestOutputMode) { - std::string short_name = - def.detailed_name.substr(def.short_name_offset, def.short_name_size); + std::string short_name( + def.detailed_name.substr(def.short_name_offset, def.short_name_size)); ReflectMember(visitor, "short_name", short_name); } else { ReflectMember(visitor, "short_name_offset", def.short_name_offset); diff --git a/src/serializer.h b/src/serializer.h index 3a0a3dcd..1d66a74b 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -1,6 +1,7 @@ #pragma once #include "maybe.h" +#include "ntstring.h" #include "port.h" #include @@ -160,8 +161,8 @@ void Reflect(Writer& visitor, std::string& value); void Reflect(Reader& visitor, std::string_view& view); void Reflect(Writer& visitor, std::string_view& view); -void Reflect(Reader& visitor, std::unique_ptr& value); -void Reflect(Writer& visitor, std::unique_ptr& value); +void Reflect(Reader& visitor, NTString& value); +void Reflect(Writer& visitor, NTString& value); // std::monostate is used to represent JSON null void Reflect(Reader& visitor, std::monostate&);