mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +00:00
.
This commit is contained in:
parent
22a7bcb47f
commit
eb7d0951ce
@ -1,120 +0,0 @@
|
||||
#include <vector>
|
||||
|
||||
struct MergeableUpdate {
|
||||
int a;
|
||||
int b;
|
||||
std::vector<int> to_add;
|
||||
};
|
||||
|
||||
/*
|
||||
TEXT_REPLACE:
|
||||
std::__1::vector <===> std::vector
|
||||
c:@N@std@ST>2#T#T@vector <===> c:@N@std@N@__1@ST>2#T#T@vector
|
||||
10956461108384510180 <===> 9178760565669096175
|
||||
|
||||
OUTPUT:
|
||||
{
|
||||
"includes": [{
|
||||
"line": 0,
|
||||
"resolved_path": "&vector"
|
||||
}],
|
||||
"skipped_by_preprocessor": [],
|
||||
"types": [{
|
||||
"id": 0,
|
||||
"usr": 14399919566014425846,
|
||||
"detailed_name": "MergeableUpdate",
|
||||
"short_name": "MergeableUpdate",
|
||||
"kind": 23,
|
||||
"declarations": [],
|
||||
"spell": "3:8-3:23|-1|1|2",
|
||||
"extent": "3:1-7:2|-1|1|0",
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [0, 1, 2],
|
||||
"instances": [],
|
||||
"uses": []
|
||||
}, {
|
||||
"id": 1,
|
||||
"usr": 17,
|
||||
"detailed_name": "",
|
||||
"short_name": "",
|
||||
"kind": 0,
|
||||
"declarations": [],
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [],
|
||||
"instances": [0, 1],
|
||||
"uses": []
|
||||
}, {
|
||||
"id": 2,
|
||||
"usr": 9178760565669096175,
|
||||
"detailed_name": "std::vector",
|
||||
"short_name": "vector",
|
||||
"kind": 0,
|
||||
"declarations": [],
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [],
|
||||
"instances": [2],
|
||||
"uses": ["6:8-6:14|-1|1|4"]
|
||||
}, {
|
||||
"id": 3,
|
||||
"usr": 5401847601697785946,
|
||||
"detailed_name": "",
|
||||
"short_name": "",
|
||||
"kind": 0,
|
||||
"declarations": [],
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [],
|
||||
"instances": [],
|
||||
"uses": ["6:3-6:6|0|2|4"]
|
||||
}],
|
||||
"funcs": [],
|
||||
"vars": [{
|
||||
"id": 0,
|
||||
"usr": 11633578660978286467,
|
||||
"detailed_name": "int MergeableUpdate::a",
|
||||
"short_name": "a",
|
||||
"declarations": [],
|
||||
"spell": "4:7-4:8|0|2|2",
|
||||
"extent": "4:3-4:8|0|2|0",
|
||||
"type": 1,
|
||||
"uses": [],
|
||||
"kind": 8,
|
||||
"storage": 0
|
||||
}, {
|
||||
"id": 1,
|
||||
"usr": 14949552147532317793,
|
||||
"detailed_name": "int MergeableUpdate::b",
|
||||
"short_name": "b",
|
||||
"declarations": [],
|
||||
"spell": "5:7-5:8|0|2|2",
|
||||
"extent": "5:3-5:8|0|2|0",
|
||||
"type": 1,
|
||||
"uses": [],
|
||||
"kind": 8,
|
||||
"storage": 0
|
||||
}, {
|
||||
"id": 2,
|
||||
"usr": 9003350345237582363,
|
||||
"detailed_name": "std::vector<int> MergeableUpdate::to_add",
|
||||
"short_name": "to_add",
|
||||
"declarations": [],
|
||||
"spell": "6:20-6:26|0|2|2",
|
||||
"extent": "6:3-6:26|0|2|0",
|
||||
"type": 2,
|
||||
"uses": [],
|
||||
"kind": 8,
|
||||
"storage": 0
|
||||
}]
|
||||
}
|
||||
*/
|
@ -1,158 +0,0 @@
|
||||
//#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct CompilationEntry {
|
||||
std::string directory;
|
||||
std::string filename;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
|
||||
std::vector<CompilationEntry> LoadCompilationEntriesFromDirectory(const std::string& project_directory);
|
||||
|
||||
/*
|
||||
TEXT_REPLACE:
|
||||
std::__1::vector <===> std::vector
|
||||
std::__1::string <===> std::string
|
||||
std::__cxx11::string <===> std::string
|
||||
c:@N@std@string <===> c:@N@std@N@__1@T@string
|
||||
c:@N@std@T@string <===> c:@N@std@N@__1@T@string
|
||||
c:@N@std@N@__cxx11@T@string <===> c:@N@std@N@__1@T@string
|
||||
c:@N@std@ST>2#T#T@vector <===> c:@N@std@N@__1@ST>2#T#T@vector
|
||||
c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@N@__1@S@basic_string>#C#$@N@std@N@__1@S@char_traits>#C#$@N@std@N@__1@S@allocator>#C# <===> c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@S@basic_string>#C#$@N@std@S@char_traits>#C#$@N@std@S@allocator>#C#
|
||||
c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@N@__cxx11@S@basic_string>#C#$@N@std@S@char_traits>#C#$@N@std@S@allocator>#C# <===> c:@F@LoadCompilationEntriesFromDirectory#&1$@N@std@S@basic_string>#C#$@N@std@S@char_traits>#C#$@N@std@S@allocator>#C#
|
||||
4160338041907786 <===> 14151982074805896770
|
||||
7543170857910783654 <===> 14151982074805896770
|
||||
9802818309312685221 <===> 11244864715202245734
|
||||
7636646237071509980 <===> 14151982074805896770
|
||||
9178760565669096175 <===> 10956461108384510180
|
||||
10468929532989002392 <===> 11244864715202245734
|
||||
4160338041907786 <===> 14151982074805896770
|
||||
9802818309312685221 <===> 11244864715202245734
|
||||
|
||||
OUTPUT:
|
||||
{
|
||||
"includes": [{
|
||||
"line": 2,
|
||||
"resolved_path": "&string"
|
||||
}, {
|
||||
"line": 3,
|
||||
"resolved_path": "&vector"
|
||||
}],
|
||||
"skipped_by_preprocessor": [],
|
||||
"types": [{
|
||||
"id": 0,
|
||||
"usr": 4992269036372211530,
|
||||
"detailed_name": "CompilationEntry",
|
||||
"short_name": "CompilationEntry",
|
||||
"kind": 23,
|
||||
"declarations": [],
|
||||
"spell": "6:8-6:24|-1|1|2",
|
||||
"extent": "6:1-10:2|-1|1|0",
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [0, 1, 2],
|
||||
"instances": [],
|
||||
"uses": ["12:13-12:29|-1|1|4"]
|
||||
}, {
|
||||
"id": 1,
|
||||
"usr": 14151982074805896770,
|
||||
"detailed_name": "std::string",
|
||||
"short_name": "string",
|
||||
"kind": 0,
|
||||
"declarations": [],
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [],
|
||||
"instances": [0, 1],
|
||||
"uses": ["7:8-7:14|-1|1|4", "8:8-8:14|-1|1|4", "9:20-9:26|-1|1|4", "12:78-12:84|-1|1|4"]
|
||||
}, {
|
||||
"id": 2,
|
||||
"usr": 5401847601697785946,
|
||||
"detailed_name": "",
|
||||
"short_name": "",
|
||||
"kind": 0,
|
||||
"declarations": [],
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [],
|
||||
"instances": [],
|
||||
"uses": ["7:3-7:6|0|2|4", "8:3-8:6|0|2|4", "9:3-9:6|0|2|4", "9:15-9:18|0|2|4", "12:1-12:4|-1|1|4", "12:73-12:76|-1|1|4"]
|
||||
}, {
|
||||
"id": 3,
|
||||
"usr": 10956461108384510180,
|
||||
"detailed_name": "std::vector",
|
||||
"short_name": "vector",
|
||||
"kind": 0,
|
||||
"declarations": [],
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"types": [],
|
||||
"funcs": [],
|
||||
"vars": [],
|
||||
"instances": [2],
|
||||
"uses": ["9:8-9:14|-1|1|4", "12:6-12:12|-1|1|4"]
|
||||
}],
|
||||
"funcs": [{
|
||||
"id": 0,
|
||||
"usr": 11244864715202245734,
|
||||
"detailed_name": "std::vector<CompilationEntry> LoadCompilationEntriesFromDirectory(const std::string &project_directory)",
|
||||
"short_name": "LoadCompilationEntriesFromDirectory",
|
||||
"kind": 12,
|
||||
"storage": 1,
|
||||
"declarations": [{
|
||||
"spell": "12:31-12:66|-1|1|1",
|
||||
"param_spellings": ["12:86-12:103"]
|
||||
}],
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"vars": [],
|
||||
"uses": [],
|
||||
"callees": []
|
||||
}],
|
||||
"vars": [{
|
||||
"id": 0,
|
||||
"usr": 1153224798516629792,
|
||||
"detailed_name": "std::string CompilationEntry::directory",
|
||||
"short_name": "directory",
|
||||
"declarations": [],
|
||||
"spell": "7:15-7:24|0|2|2",
|
||||
"extent": "7:3-7:24|0|2|0",
|
||||
"type": 1,
|
||||
"uses": [],
|
||||
"kind": 8,
|
||||
"storage": 0
|
||||
}, {
|
||||
"id": 1,
|
||||
"usr": 2255668374222866345,
|
||||
"detailed_name": "std::string CompilationEntry::filename",
|
||||
"short_name": "filename",
|
||||
"declarations": [],
|
||||
"spell": "8:15-8:23|0|2|2",
|
||||
"extent": "8:3-8:23|0|2|0",
|
||||
"type": 1,
|
||||
"uses": [],
|
||||
"kind": 8,
|
||||
"storage": 0
|
||||
}, {
|
||||
"id": 2,
|
||||
"usr": 12616880765274259414,
|
||||
"detailed_name": "std::vector<std::string> CompilationEntry::args",
|
||||
"short_name": "args",
|
||||
"declarations": [],
|
||||
"spell": "9:28-9:32|0|2|2",
|
||||
"extent": "9:3-9:32|0|2|0",
|
||||
"type": 3,
|
||||
"uses": [],
|
||||
"kind": 8,
|
||||
"storage": 0
|
||||
}]
|
||||
}
|
||||
*/
|
@ -47,10 +47,10 @@ struct RealCacheManager : ICacheManager {
|
||||
std::string cache_file;
|
||||
size_t len = config_->projectRoot.size();
|
||||
if (StartsWith(source_file, config_->projectRoot)) {
|
||||
cache_file = EscapeFileName(config_->projectRoot) + '/' +
|
||||
cache_file = EscapeFileName(config_->projectRoot) +
|
||||
EscapeFileName(source_file.substr(len));
|
||||
} else {
|
||||
cache_file = '@' + EscapeFileName(config_->projectRoot) + '/' +
|
||||
cache_file = '@' + EscapeFileName(config_->projectRoot) +
|
||||
EscapeFileName(source_file);
|
||||
}
|
||||
|
||||
|
@ -673,7 +673,7 @@ void QueryDb_DoIdMap(QueueManager* queue,
|
||||
// it, load the previous state from disk and rerun IdMap logic later. Do not
|
||||
// do this if we have already attempted in the past.
|
||||
if (!request->load_previous && !request->previous &&
|
||||
db->usr_to_file.find(NormalizedPath(request->current->path)) !=
|
||||
db->usr_to_file.find(LowerPathIfInsensitive(request->current->path)) !=
|
||||
db->usr_to_file.end()) {
|
||||
assert(!request->load_previous);
|
||||
request->load_previous = true;
|
||||
@ -740,7 +740,7 @@ void QueryDb_OnIndexed(QueueManager* queue,
|
||||
|
||||
// Semantic highlighting.
|
||||
QueryFileId file_id =
|
||||
db->usr_to_file[NormalizedPath(working_file->filename)];
|
||||
db->usr_to_file[LowerPathIfInsensitive(working_file->filename)];
|
||||
QueryFile* file = &db->files[file_id.id];
|
||||
EmitSemanticHighlighting(db, semantic_cache, working_file, file);
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ bool FindFileOrFail(QueryDatabase* db,
|
||||
QueryFileId* out_file_id) {
|
||||
*out_query_file = nullptr;
|
||||
|
||||
auto it = db->usr_to_file.find(NormalizedPath(absolute_path));
|
||||
auto it = db->usr_to_file.find(LowerPathIfInsensitive(absolute_path));
|
||||
if (it != db->usr_to_file.end()) {
|
||||
QueryFile& file = db->files[it->second.id];
|
||||
if (file.def) {
|
||||
|
27
src/query.cc
27
src/query.cc
@ -305,7 +305,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IdMap& id_map,
|
||||
Maybe<QueryFileId> GetQueryFileIdFromPath(QueryDatabase* query_db,
|
||||
const std::string& path,
|
||||
bool create_if_missing) {
|
||||
NormalizedPath normalized_path(path);
|
||||
std::string normalized_path = LowerPathIfInsensitive(path);
|
||||
auto it = query_db->usr_to_file.find(normalized_path);
|
||||
if (it != query_db->usr_to_file.end())
|
||||
return QueryFileId(it->second.id);
|
||||
@ -730,33 +730,12 @@ std::string IndexUpdate::ToString() {
|
||||
return output.GetString();
|
||||
}
|
||||
|
||||
NormalizedPath::NormalizedPath(const std::string& path)
|
||||
: path(LowerPathIfCaseInsensitive(path)) {}
|
||||
|
||||
bool NormalizedPath::operator==(const NormalizedPath& rhs) const {
|
||||
return path == rhs.path;
|
||||
}
|
||||
|
||||
bool NormalizedPath::operator!=(const NormalizedPath& rhs) const {
|
||||
return path != rhs.path;
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// QUERYDB THREAD FUNCTIONS
|
||||
// ------------------------
|
||||
|
||||
void QueryDatabase::RemoveUsrs(SymbolKind usr_kind,
|
||||
const std::vector<Usr>& to_remove) {
|
||||
// This function runs on the querydb thread.
|
||||
|
||||
// When we remove an element, we just erase the state from the storage. We do
|
||||
// not update array indices because that would take a huge amount of time for
|
||||
// a very large index.
|
||||
//
|
||||
// There means that there is some memory growth that will never be reclaimed,
|
||||
// but it should be pretty minimal and is solved by simply restarting the
|
||||
// indexer and loading from cache, which is a fast operation.
|
||||
|
||||
switch (usr_kind) {
|
||||
case SymbolKind::Type: {
|
||||
for (const Usr& usr : to_remove) {
|
||||
@ -816,7 +795,7 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
|
||||
}
|
||||
|
||||
for (const std::string& filename : update->files_removed)
|
||||
files[usr_to_file[NormalizedPath(filename)].id].def = std::nullopt;
|
||||
files[usr_to_file[LowerPathIfInsensitive(filename)].id].def = std::nullopt;
|
||||
ImportOrUpdate(update->files_def_update);
|
||||
|
||||
RemoveUsrs(SymbolKind::Type, update->types_removed);
|
||||
@ -845,7 +824,7 @@ void QueryDatabase::ImportOrUpdate(
|
||||
// This function runs on the querydb thread.
|
||||
|
||||
for (auto& def : updates) {
|
||||
auto it = usr_to_file.find(NormalizedPath(def.value.path));
|
||||
auto it = usr_to_file.find(LowerPathIfInsensitive(def.value.path));
|
||||
assert(it != usr_to_file.end());
|
||||
|
||||
QueryFile& existing = files[it->second.id];
|
||||
|
11
src/query.h
11
src/query.h
@ -257,15 +257,6 @@ MAKE_REFLECT_STRUCT(IndexUpdate,
|
||||
vars_declarations,
|
||||
vars_uses);
|
||||
|
||||
struct NormalizedPath {
|
||||
explicit NormalizedPath(const std::string& path);
|
||||
bool operator==(const NormalizedPath& rhs) const;
|
||||
bool operator!=(const NormalizedPath& rhs) const;
|
||||
|
||||
std::string path;
|
||||
};
|
||||
MAKE_HASHABLE(NormalizedPath, t.path);
|
||||
|
||||
// The query database is heavily optimized for fast queries. It is stored
|
||||
// in-memory.
|
||||
struct QueryDatabase {
|
||||
@ -279,7 +270,7 @@ struct QueryDatabase {
|
||||
std::vector<QueryVar> vars;
|
||||
|
||||
// Lookup symbol based on a usr.
|
||||
spp::sparse_hash_map<NormalizedPath, QueryFileId> usr_to_file;
|
||||
spp::sparse_hash_map<std::string, QueryFileId> usr_to_file;
|
||||
spp::sparse_hash_map<Usr, QueryTypeId> usr_to_type;
|
||||
spp::sparse_hash_map<Usr, QueryFuncId> usr_to_func;
|
||||
spp::sparse_hash_map<Usr, QueryVarId> usr_to_var;
|
||||
|
@ -449,8 +449,6 @@ TEST_SUITE("Serializer utils") {
|
||||
REQUIRE(GetBaseName("foo/foo.cc") == "foo.cc");
|
||||
REQUIRE(GetBaseName("/foo.cc") == "foo.cc");
|
||||
REQUIRE(GetBaseName("///foo.cc") == "foo.cc");
|
||||
REQUIRE(GetBaseName("bar/") == "bar/");
|
||||
REQUIRE(GetBaseName("foobar/bar/") ==
|
||||
"foobar/bar/"); // TODO: Should be bar, but good enough.
|
||||
REQUIRE(GetBaseName("bar/") == ".");
|
||||
}
|
||||
}
|
||||
|
87
src/test.cc
87
src/test.cc
@ -81,38 +81,6 @@ void ParseTestExpectation(
|
||||
*/
|
||||
#endif
|
||||
|
||||
// Scan for TEXT_REPLACE:
|
||||
{
|
||||
bool in_output = false;
|
||||
for (std::string line : lines_with_endings) {
|
||||
TrimInPlace(line);
|
||||
|
||||
if (StartsWith(line, "TEXT_REPLACE:")) {
|
||||
assert(!in_output && "multiple TEXT_REPLACE sections");
|
||||
in_output = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_output && line.empty())
|
||||
break;
|
||||
|
||||
if (in_output) {
|
||||
static const std::string kKey = " <===> ";
|
||||
size_t index = line.find(kKey);
|
||||
LOG_IF_S(FATAL, index == std::string::npos)
|
||||
<< " No '" << kKey << "' in replacement string '" << line << "'"
|
||||
<< ", index=" << index;
|
||||
|
||||
TextReplacer::Replacement replacement;
|
||||
replacement.from = line.substr(0, index);
|
||||
replacement.to = line.substr(index + kKey.size());
|
||||
TrimInPlace(replacement.from);
|
||||
TrimInPlace(replacement.to);
|
||||
replacer->replacements.push_back(replacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan for EXTRA_FLAGS:
|
||||
{
|
||||
bool in_output = false;
|
||||
@ -461,58 +429,3 @@ bool RunIndexTests(const std::string& filter_path, bool enable_update) {
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// TODO: ctor/dtor, copy ctor
|
||||
// TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile&
|
||||
// refs.
|
||||
|
||||
TEST_SUITE("ParseTestExpectation") {
|
||||
TEST_CASE("Parse TEXT_REPLACE") {
|
||||
// clang-format off
|
||||
std::vector<std::string> lines_with_endings = {
|
||||
"/*\n",
|
||||
"TEXT_REPLACE:\n",
|
||||
" foo <===> \tbar \n",
|
||||
"01 <===> 2\n",
|
||||
"\n",
|
||||
"*/\n"};
|
||||
// clang-format on
|
||||
|
||||
TextReplacer text_replacer;
|
||||
std::vector<std::string> flags;
|
||||
std::unordered_map<std::string, std::string> all_expected_output;
|
||||
ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags,
|
||||
&all_expected_output);
|
||||
|
||||
REQUIRE(text_replacer.replacements.size() == 2);
|
||||
REQUIRE(text_replacer.replacements[0].from == "foo");
|
||||
REQUIRE(text_replacer.replacements[0].to == "bar");
|
||||
REQUIRE(text_replacer.replacements[1].from == "01");
|
||||
REQUIRE(text_replacer.replacements[1].to == "2");
|
||||
}
|
||||
|
||||
TEST_CASE("Apply TEXT_REPLACE") {
|
||||
TextReplacer replacer;
|
||||
replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"});
|
||||
replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"});
|
||||
replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"});
|
||||
|
||||
// Equal-length.
|
||||
REQUIRE(replacer.Apply("foo") == "bar");
|
||||
REQUIRE(replacer.Apply("bar") == "bar");
|
||||
|
||||
// Shorter replacement.
|
||||
REQUIRE(replacer.Apply("01") == "2");
|
||||
REQUIRE(replacer.Apply("2") == "2");
|
||||
|
||||
// Longer replacement.
|
||||
REQUIRE(replacer.Apply("3") == "456");
|
||||
REQUIRE(replacer.Apply("456") == "456");
|
||||
|
||||
// Content before-after replacement.
|
||||
REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb");
|
||||
|
||||
// Multiple replacements.
|
||||
REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456");
|
||||
}
|
||||
}
|
||||
|
155
src/utils.cc
155
src/utils.cc
@ -7,16 +7,17 @@
|
||||
#include <siphash.h>
|
||||
#include <loguru/loguru.hpp>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
using namespace std::placeholders;
|
||||
|
||||
// DEFAULT_RESOURCE_DIRECTORY is passed with quotes for non-MSVC compilers, ie,
|
||||
// foo vs "foo".
|
||||
@ -28,32 +29,16 @@
|
||||
#endif
|
||||
|
||||
void TrimInPlace(std::string& s) {
|
||||
s.erase(s.begin(),
|
||||
std::find_if(s.begin(), s.end(),
|
||||
std::not1(std::ptr_fun<int, int>(std::isspace))));
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(),
|
||||
std::not1(std::ptr_fun<int, int>(std::isspace)))
|
||||
.base(),
|
||||
s.end());
|
||||
auto f = [](char c) { return !isspace(c); };
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
|
||||
}
|
||||
std::string Trim(std::string s) {
|
||||
TrimInPlace(s);
|
||||
return s;
|
||||
}
|
||||
void RemoveLastCR(std::string& s) {
|
||||
if (!s.empty() && *s.rbegin() == '\r')
|
||||
s.pop_back();
|
||||
}
|
||||
|
||||
uint64_t HashUsr(const std::string& s) {
|
||||
return HashUsr(s.c_str(), s.size());
|
||||
}
|
||||
|
||||
uint64_t HashUsr(const char* s) {
|
||||
return HashUsr(s, strlen(s));
|
||||
}
|
||||
|
||||
uint64_t HashUsr(const char* s, size_t n) {
|
||||
uint64_t HashUsr(std::string_view s) {
|
||||
union {
|
||||
uint64_t ret;
|
||||
uint8_t out[8];
|
||||
@ -61,43 +46,31 @@ uint64_t HashUsr(const char* s, size_t n) {
|
||||
// k is an arbitrary key. Don't change it.
|
||||
const uint8_t k[16] = {0xd0, 0xe5, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52,
|
||||
0x61, 0x79, 0xea, 0x70, 0xca, 0x70, 0xf0, 0x0d};
|
||||
(void)siphash(reinterpret_cast<const uint8_t*>(s), n, k, out, 8);
|
||||
(void)siphash(reinterpret_cast<const uint8_t*>(s.data()), s.size(), k, out, 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// See http://stackoverflow.com/a/2072890
|
||||
bool EndsWith(std::string_view value, std::string_view ending) {
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
bool EndsWith(std::string_view s, std::string_view suffix) {
|
||||
return s.size() >= suffix.size() &&
|
||||
std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
|
||||
}
|
||||
|
||||
bool StartsWith(std::string_view value, std::string_view start) {
|
||||
if (start.size() > value.size())
|
||||
return false;
|
||||
return std::equal(start.begin(), start.end(), value.begin());
|
||||
bool StartsWith(std::string_view s, std::string_view prefix) {
|
||||
return s.size() >= prefix.size() &&
|
||||
std::equal(prefix.begin(), prefix.end(), s.begin());
|
||||
}
|
||||
|
||||
bool AnyStartsWith(const std::vector<std::string>& values,
|
||||
const std::string& start) {
|
||||
return std::any_of(
|
||||
std::begin(values), std::end(values),
|
||||
[&start](const std::string& value) { return StartsWith(value, start); });
|
||||
bool AnyStartsWith(const std::vector<std::string>& xs,
|
||||
std::string_view prefix) {
|
||||
return std::any_of(xs.begin(), xs.end(), std::bind(StartsWith, _1, prefix));
|
||||
}
|
||||
|
||||
bool StartsWithAny(const std::string& value,
|
||||
const std::vector<std::string>& startings) {
|
||||
return std::any_of(std::begin(startings), std::end(startings),
|
||||
[&value](const std::string& starting) {
|
||||
return StartsWith(value, starting);
|
||||
});
|
||||
bool StartsWithAny(std::string_view s, const std::vector<std::string>& ps) {
|
||||
return std::any_of(ps.begin(), ps.end(), std::bind(StartsWith, s, _1));
|
||||
}
|
||||
|
||||
bool EndsWithAny(const std::string& value,
|
||||
const std::vector<std::string>& endings) {
|
||||
return std::any_of(
|
||||
std::begin(endings), std::end(endings),
|
||||
[&value](const std::string& ending) { return EndsWith(value, ending); });
|
||||
bool EndsWithAny(std::string_view s, const std::vector<std::string>& ss) {
|
||||
return std::any_of(ss.begin(), ss.end(), std::bind(EndsWith, s, _1));
|
||||
}
|
||||
|
||||
bool FindAnyPartial(const std::string& value,
|
||||
@ -120,10 +93,7 @@ std::string GetDirName(std::string path) {
|
||||
}
|
||||
|
||||
std::string GetBaseName(const std::string& path) {
|
||||
size_t last_slash = path.find_last_of('/');
|
||||
if (last_slash != std::string::npos && (last_slash + 1) < path.size())
|
||||
return path.substr(last_slash + 1);
|
||||
return path;
|
||||
return fs::path(path).filename();
|
||||
}
|
||||
|
||||
std::string StripFileType(const std::string& path) {
|
||||
@ -149,13 +119,15 @@ std::vector<std::string> SplitString(const std::string& str,
|
||||
return strings;
|
||||
}
|
||||
|
||||
std::string LowerPathIfCaseInsensitive(const std::string& path) {
|
||||
std::string result = path;
|
||||
std::string LowerPathIfInsensitive(const std::string& path) {
|
||||
#if defined(_WIN32)
|
||||
for (size_t i = 0; i < result.size(); ++i)
|
||||
result[i] = (char)tolower(result[i]);
|
||||
std::string ret = path;
|
||||
for (char& c : ret)
|
||||
c = tolower(c);
|
||||
return ret;
|
||||
#else
|
||||
return path;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static void GetFilesInFolderHelper(
|
||||
@ -164,7 +136,7 @@ static void GetFilesInFolderHelper(
|
||||
std::string output_prefix,
|
||||
const std::function<void(const std::string&)>& handler) {
|
||||
std::queue<std::pair<fs::path, fs::path>> q;
|
||||
q.push(std::make_pair(fs::path(folder), fs::path(output_prefix)));
|
||||
q.emplace(fs::path(folder), fs::path(output_prefix));
|
||||
while (!q.empty()) {
|
||||
for (auto it = fs::directory_iterator(q.front().first); it != fs::directory_iterator(); ++it) {
|
||||
auto path = it->path();
|
||||
@ -212,58 +184,29 @@ void EnsureEndsInSlash(std::string& path) {
|
||||
}
|
||||
|
||||
std::string EscapeFileName(std::string path) {
|
||||
if (path.size() && path.back() == '/')
|
||||
path.pop_back();
|
||||
std::replace(path.begin(), path.end(), '\\', '@');
|
||||
std::replace(path.begin(), path.end(), '/', '@');
|
||||
std::replace(path.begin(), path.end(), ':', '@');
|
||||
bool slash = path.size() && path.back() == '/';
|
||||
for (char& c : path)
|
||||
if (c == '\\' || c == '/' || c == ':')
|
||||
c = '@';
|
||||
if (slash)
|
||||
path += '/';
|
||||
return path;
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/a/6089413
|
||||
std::istream& SafeGetline(std::istream& is, std::string& t) {
|
||||
t.clear();
|
||||
|
||||
// The characters in the stream are read one-by-one using a std::streambuf.
|
||||
// That is faster than reading them one-by-one using the std::istream. Code
|
||||
// that uses streambuf this way must be guarded by a sentry object. The sentry
|
||||
// object performs various tasks, such as thread synchronization and updating
|
||||
// the stream state.
|
||||
|
||||
std::istream::sentry se(is, true);
|
||||
std::streambuf* sb = is.rdbuf();
|
||||
|
||||
for (;;) {
|
||||
int c = sb->sbumpc();
|
||||
if (c == EOF) {
|
||||
// Also handle the case when the last line has no line ending
|
||||
if (t.empty())
|
||||
is.setstate(std::ios::eofbit);
|
||||
return is;
|
||||
}
|
||||
|
||||
t += (char)c;
|
||||
|
||||
if (c == '\n')
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileExists(const std::string& filename) {
|
||||
return fs::exists(filename);
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadContent(const std::string& filename) {
|
||||
LOG_S(INFO) << "Reading " << filename;
|
||||
std::ifstream cache;
|
||||
cache.open(filename);
|
||||
|
||||
try {
|
||||
return std::string(std::istreambuf_iterator<char>(cache),
|
||||
std::istreambuf_iterator<char>());
|
||||
} catch (std::ios_base::failure&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
char buf[4096];
|
||||
std::string ret;
|
||||
FILE* f = fopen(filename.c_str(), "rb");
|
||||
if (!f) return {};
|
||||
size_t n;
|
||||
while ((n = fread(buf, 1, sizeof buf, f)) > 0)
|
||||
ret.append(buf, n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> ReadFileLines(std::string filename) {
|
||||
@ -302,14 +245,12 @@ std::string TextReplacer::Apply(const std::string& content) {
|
||||
}
|
||||
|
||||
void WriteToFile(const std::string& filename, const std::string& content) {
|
||||
std::ofstream file(filename,
|
||||
std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
if (!file.good()) {
|
||||
FILE* f = fopen(filename.c_str(), "wb");
|
||||
if (!f || fwrite(content.c_str(), content.size(), 1, f) != 1) {
|
||||
LOG_S(ERROR) << "Cannot write to " << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
file << content;
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
std::string GetDefaultResourceDirectory() {
|
||||
|
15
src/utils.h
15
src/utils.h
@ -13,19 +13,14 @@
|
||||
void TrimInPlace(std::string& s);
|
||||
std::string Trim(std::string s);
|
||||
|
||||
uint64_t HashUsr(const std::string& s);
|
||||
uint64_t HashUsr(const char* s);
|
||||
uint64_t HashUsr(const char* s, size_t n);
|
||||
uint64_t HashUsr(std::string_view s);
|
||||
|
||||
// Returns true if |value| starts/ends with |start| or |ending|.
|
||||
bool StartsWith(std::string_view value, std::string_view start);
|
||||
bool EndsWith(std::string_view value, std::string_view ending);
|
||||
bool AnyStartsWith(const std::vector<std::string>& values,
|
||||
const std::string& start);
|
||||
bool StartsWithAny(const std::string& value,
|
||||
const std::vector<std::string>& startings);
|
||||
bool EndsWithAny(const std::string& value,
|
||||
const std::vector<std::string>& endings);
|
||||
bool AnyStartsWith(const std::vector<std::string>& xs, std::string_view prefix);
|
||||
bool StartsWithAny(std::string_view s, const std::vector<std::string>& ps);
|
||||
bool EndsWithAny(std::string_view s, const std::vector<std::string>& ss);
|
||||
bool FindAnyPartial(const std::string& value,
|
||||
const std::vector<std::string>& values);
|
||||
// Returns the dirname of |path|, i.e. "foo/bar.cc" => "foo", "foo" => ".",
|
||||
@ -39,7 +34,7 @@ std::string StripFileType(const std::string& path);
|
||||
std::vector<std::string> SplitString(const std::string& str,
|
||||
const std::string& delimiter);
|
||||
|
||||
std::string LowerPathIfCaseInsensitive(const std::string& path);
|
||||
std::string LowerPathIfInsensitive(const std::string& path);
|
||||
|
||||
template <typename TValues, typename TMap>
|
||||
std::string StringJoinMap(const TValues& values,
|
||||
|
Loading…
Reference in New Issue
Block a user