Skip to content

Commit 410d4e5

Browse files
committed
squash commit for v2.1.14
1 parent a9b7817 commit 410d4e5

382 files changed

Lines changed: 5643 additions & 49680 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ check_type_size(int SIZEOF_INT)
128128

129129
check_cxx_compiler_flag("-std=gnu++14" HAS_GNUPP14)
130130
check_cxx_compiler_flag("-std=gnu++17" HAS_GNUPP17)
131-
if(HAS_GNUPP17 OR ${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
131+
check_cxx_compiler_flag("-std=gnu++20" HAS_GNUPP20)
132+
if(HAS_GNUPP20 OR ${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
133+
set_cxx_standard(20 "-std=gnu++20")
134+
elseif(HAS_GNUPP17)
132135
set_cxx_standard(17 "-std=gnu++17")
133136
elseif (HAS_GNUPP14)
134137
set_cxx_standard(14 "-std=gnu++14")
@@ -183,6 +186,11 @@ endif()
183186

184187
add_library(arch_generic OBJECT ${DISPATCH_OBJECTS})
185188
target_compile_options(arch_generic PUBLIC -DDISPATCH_ARCH=ARCH_GENERIC -DARCH_ID=0 -DEigen=Eigen_GENERIC)
189+
#target_compile_options(arch_generic PUBLIC -DDISPATCH_ARCH=ARCH_GENERIC -DARCH_ID=0)
190+
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
191+
target_compile_options(arch_generic PUBLIC /Zc:__cplusplus)
192+
endif()
193+
186194
if(X86)
187195
add_library(arch_sse4_1 OBJECT ${DISPATCH_OBJECTS})
188196
add_library(arch_avx2 OBJECT ${DISPATCH_OBJECTS})
@@ -192,14 +200,18 @@ if(X86)
192200
add_definitions(-DWITH_AVX2)
193201
add_definitions(-DWITH_SSE4_1)
194202
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
195-
target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_SSE4_1)
196-
target_compile_options(arch_avx2 PUBLIC -DDISPATCH_ARCH=ARCH_AVX2 -DARCH_ID=2 /arch:AVX2 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_AVX2)
203+
target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_SSE4_1 /Zc:__cplusplus)
204+
target_compile_options(arch_avx2 PUBLIC -DDISPATCH_ARCH=ARCH_AVX2 -DARCH_ID=2 /arch:AVX2 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_AVX2 /Zc:__cplusplus)
205+
#target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ /Zc:__cplusplus)
206+
#target_compile_options(arch_avx2 PUBLIC -DDISPATCH_ARCH=ARCH_AVX2 -DARCH_ID=2 /arch:AVX2 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ /Zc:__cplusplus)
197207
if(WITH_AVX512)
198208
target_compile_options(arch_avx512 PUBLIC -DDISPATCH_ARCH=ARCH_AVX512 -DARCH_ID=3 /arch:AVX512 -D__SSSE3__ -D__SSE4_1__ -D__POPCNT__ -DEigen=Eigen_AVX512)
199209
endif()
200210
else()
201211
target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -mssse3 -mpopcnt -msse4.1 -DEigen=Eigen_SSE4_1)
202212
target_compile_options(arch_avx2 PUBLIC -DDISPATCH_ARCH=ARCH_AVX2 -DARCH_ID=2 -mssse3 -mpopcnt -msse4.1 -msse4.2 -mavx -mavx2 -DEigen=Eigen_AVX2)
213+
#target_compile_options(arch_sse4_1 PUBLIC -DDISPATCH_ARCH=ARCH_SSE4_1 -DARCH_ID=1 -mssse3 -mpopcnt -msse4.1)
214+
#target_compile_options(arch_avx2 PUBLIC -DDISPATCH_ARCH=ARCH_AVX2 -DARCH_ID=2 -mssse3 -mpopcnt -msse4.1 -msse4.2 -mavx -mavx2)
203215
if(WITH_AVX512)
204216
target_compile_options(arch_avx512 PUBLIC -DDISPATCH_ARCH=ARCH_AVX512 -DARCH_ID=3 -mssse3 -mpopcnt -msse4.1 -msse4.2 -mavx -mavx2 -mavx512f -mavx512bw -DEigen=Eigen_AVX512)
205217
endif()
@@ -327,20 +339,11 @@ set(OBJECTS
327339
src/lib/blast/blast_seg.cpp
328340
src/lib/blast/blast_filter.cpp
329341
src/lib/blast/nlm_linear_algebra.cpp
330-
src/stats/matrices/blosum45.cpp
331-
src/stats/matrices/blosum50.cpp
332-
src/stats/matrices/blosum62.cpp
333-
src/stats/matrices/blosum80.cpp
334-
src/stats/matrices/blosum90.cpp
335-
src/stats/matrices/pam250.cpp
336-
src/stats/matrices/pam30.cpp
337-
src/stats/matrices/pam70.cpp
338342
src/stats/stats.cpp
339343
src/stats/cbs.cpp
340344
src/stats/comp_based_stats.cpp
341345
src/stats/hauser_correction.cpp
342346
src/stats/matrix_adjust.cpp
343-
src/stats/matrix_adjust_eigen.cpp
344347
src/data/index.cpp
345348
src/data/dmnd/dmnd.cpp
346349
src/data/sequence_file.cpp
@@ -380,14 +383,17 @@ set(OBJECTS
380383
src/util/tsv/merge.cpp
381384
src/util/tsv/join.cpp
382385
src/dp/scalar/smith_waterman.cpp
383-
src/align/short.cpp
384386
src/tools/tsv.cpp
385-
src/cluster/external/make_seed_table.cpp
386387
src/cluster/external/external.cpp
387388
src/cluster/external/align.cpp
388389
src/cluster/external/cluster.cpp
389390
src/cluster/external/output.cpp
390-
)
391+
src/util/algo/pagerank.cpp
392+
src/util/algo/mcl.cpp
393+
src/stats/blast/matrix_adjust.cpp
394+
src/stats/blast/ncbi.cpp
395+
src/search/hit_buffer.cpp
396+
)
391397

392398
if(WITH_DNA)
393399
list(APPEND OBJECTS
@@ -494,6 +500,7 @@ endif()
494500
if(WITH_ZSTD OR BLAST_INCLUDE_DIR)
495501
find_path(${ZSTD_INCLUDE_DIR} NAMES zstd.h PATHS ${ZSTD_INCLUDE_DIR})
496502
find_library(ZSTD_LIBRARY NAMES libzstd.a libzstd_static)
503+
message("zstd ${ZSTD_LIBRARY}")
497504
target_include_directories(diamond PRIVATE "${ZSTD_INCLUDE_DIR}")
498505
target_link_libraries(diamond ${ZSTD_LIBRARY})
499506
add_definitions(-DWITH_ZSTD)
@@ -522,4 +529,4 @@ SET(SP -DTEST_DIR=${CMAKE_SOURCE_DIR}/src/test -P ${CMAKE_SOURCE_DIR}/src/test/t
522529
add_test(NAME blastp COMMAND ${CMAKE_COMMAND} -DNAME=blastp "-DARGS=blastp -q ${TD}/1.faa -d ${TD}/2.faa -p1" ${SP})
523530
add_test(NAME blastp-mid-sens COMMAND ${CMAKE_COMMAND} -DNAME=blastp-mid-sens "-DARGS=blastp -q ${TD}/3.faa -d ${TD}/4.faa --mid-sensitive -p1" ${SP})
524531
add_test(NAME blastp-f0 COMMAND ${CMAKE_COMMAND} -DNAME=blastp-f0 "-DARGS=blastp -q ${TD}/1.faa -d ${TD}/2.faa -f0 -p1" ${SP})
525-
add_test(NAME diamond COMMAND diamond test)
532+
add_test(NAME diamond COMMAND diamond test)

src/ChangeLog

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,29 @@
1+
[2.1.14]
2+
- Improved the performance and sensitivity of the `cluster`, `deepclust` and
3+
`linclust` workflows.
4+
- The `getseq` workflow now supports BLAST databases.
5+
- Disabled clearing of the page cache for mmaped pages when using BLAST
6+
databases.
7+
- Reduced memory use when the `sallseqid` and `staxids` output fields are
8+
used.
9+
- Fixed a bug that could cause a crash when using the JSON output format and
10+
processing the database in multiple blocks.
11+
- Fixed a bug that could cause extra commas in the JSON output format.
12+
- Fixed a bug that could cause extra quotation marks when using the `stitle`
13+
output field for the JSON output format.
14+
- The `prepdb` workflow is deprecated, and no longer needed to use BLAST
15+
databases.
16+
- The `--anchored-swipe` option can be applied to linear mode clustering
17+
rounds and searches.
18+
- The `--anchored-swipe` option can be used without compiling with `-DEXTRA=ON`
19+
(only available on systems that support AVX2).
20+
- Improved the performace of compositional matrix adjustment.
21+
122
[2.1.13]
223
- Fixed an invalid error message for the `cluster`, `deepclust` and `linclust`
324
workflows.
425
- Added the option `--oid-output` to output OIDs instead of accessions for
5-
the clustering workflows, reducing its memory use.
26+
the clustering workflows, reducing their memory use.
627
- Added support for using the `--multiprocessing` feature on Windows.
728
- Using `--multiprocessing` requires explicitly setting `--parallel-tmpdir`.
829
- Fixed a bug that could cause a crash when the `--target-indexed` option was

src/align/align.cpp

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2424
#include "output/output_format.h"
2525
#include "output/output.h"
2626
#include "legacy/pipeline.h"
27-
#include "util/async_buffer.h"
27+
#include "search/hit_buffer.h"
2828
#include "util/parallel/thread_pool.h"
2929
#include "extend.h"
3030
#include "util/util.h"
31+
//#include "util/sort/sort.h"
3132
#ifdef WITH_DNA
3233
#include "../dna/extension.h"
3334
#endif
3435
#include "util/util.h"
3536
#define _REENTRANT
3637
#include "ips4o/ips4o.hpp"
3738
#include "data/queries.h"
39+
#include "data/sequence_file.h"
40+
#include "search/hit_buffer.h"
3841

3942
using std::get;
4043
using std::tuple;
@@ -47,12 +50,6 @@ using std::vector;
4750

4851
DpStat dp_stat;
4952

50-
namespace Extension {
51-
52-
TextBuffer* pipeline_short(BlockId query, Search::Hit* begin, Search::Hit* end, Search::Config& cfg, Statistics& stats);
53-
54-
}
55-
5653
static vector<int64_t> make_partition(Search::Hit* begin, Search::Hit* end) {
5754
vector<int64_t> partition;
5855
partition.reserve(div_up(end - begin, (ptrdiff_t)config.min_task_trace_pts) + 1);
@@ -124,9 +121,9 @@ struct HitIterator {
124121
static TextBuffer* legacy_pipeline(const HitIterator::Hits& hits, Search::Config& cfg, Statistics &stat) {
125122
if (hits.end == hits.begin) {
126123
TextBuffer *buf = nullptr;
127-
if (!cfg.blocked_processing && *cfg.output_format != OutputFormat::daa && config.report_unaligned != 0) {
124+
if (!cfg.blocked_processing && *cfg.output_format != OutputFormat::daa && cfg.output_format->report_unaligned()) {
128125
buf = new TextBuffer;
129-
Output::Info info{ cfg.query->seq_info(hits.query), true, cfg.db.get(), *buf, {}, Util::Seq::AccessionParsing(), cfg.db->sequence_count(), cfg.db->letters() };
126+
Output::Info info{ cfg.query->seq_info(hits.query), true, cfg.db.get(), *buf, Util::Seq::AccessionParsing(), cfg.db->sequence_count(), cfg.db->letters() };
130127
cfg.output_format->print_query_intro(info);
131128
cfg.output_format->print_query_epilog(info);
132129
}
@@ -161,6 +158,7 @@ static TextBuffer* legacy_pipeline(const HitIterator::Hits& hits, Search::Config
161158
static void align_worker(HitIterator* hit_it, Search::Config* cfg, int64_t next)
162159
{
163160
try {
161+
std::pmr::monotonic_buffer_resource pool;
164162
const vector<HitIterator::Hits> hits = hit_it->fetch(next);
165163
assert(!hits.empty());
166164
Statistics stat;
@@ -173,23 +171,18 @@ static void align_worker(HitIterator* hit_it, Search::Config* cfg, int64_t next)
173171
output_sink->push(h->query, buf);
174172
continue;
175173
}
176-
if (config.pipeline_short) {
177-
TextBuffer* buf = Extension::pipeline_short(h->query, h->begin, h->end, *cfg, stat);
178-
output_sink->push(h->query, buf);
179-
continue;
180-
}
181174
if (h->begin == nullptr && !HitIterator::single_query()) {
182175
output_sink->push(h->query, nullptr);
183176
continue;
184177
}
185178

186-
pair<vector<Extension::Match>, Extension::Stats> matches =
179+
vector<Extension::Match> matches =
187180
#ifdef WITH_DNA
188181
align_mode.mode == AlignMode::blastn ? Dna::extend(*cfg, cfg->query->seqs()[h->query]) :
189182
#endif
190-
Extension::extend(h->query, h->begin, h->end, *cfg, stat, parallel ? DP::Flags::PARALLEL : DP::Flags::NONE);
191-
TextBuffer* buf = cfg->blocked_processing ? Extension::generate_intermediate_output(matches.first, h->query, *cfg) : Extension::generate_output(matches.first, matches.second, h->query, stat, *cfg);
192-
if (!matches.first.empty() && cfg->track_aligned_queries) {
183+
Extension::extend(h->query, h->begin, h->end, *cfg, stat, parallel ? DP::Flags::PARALLEL : DP::Flags::NONE, pool);
184+
TextBuffer* buf = cfg->blocked_processing ? Extension::generate_intermediate_output(matches, h->query, *cfg) : Extension::generate_output(matches, h->query, stat, *cfg);
185+
if (!matches.empty() && cfg->track_aligned_queries) {
193186
std::lock_guard<std::mutex> lock(query_aligned_mtx);
194187
if (!query_aligned[h->query]) {
195188
query_aligned[h->query] = true;
@@ -198,7 +191,7 @@ static void align_worker(HitIterator* hit_it, Search::Config* cfg, int64_t next)
198191
}
199192
if (!config.unaligned_targets.empty()) {
200193
lock_guard<mutex> lock(cfg->aligned_targets_mtx);
201-
for (const Extension::Match &m : matches.first) {
194+
for (const Extension::Match &m : matches) {
202195
const OId oid = cfg->target->block_id2oid(m.target_block_id);
203196
if (!cfg->aligned_targets[oid])
204197
cfg->aligned_targets[oid] = true;
@@ -220,42 +213,48 @@ void align_queries(Consumer* output_file, Search::Config& cfg)
220213
const int64_t mem_limit = Util::String::interpret_number(config.memory_limit.get("16G"));
221214

222215
pair<BlockId, BlockId> query_range;
223-
TaskTimer timer(nullptr, 3);
216+
TaskTimer timer("Allocating memory", 3);
224217

225218
if (!cfg.blocked_processing && !cfg.iterated())
226219
cfg.db->init_random_access(cfg.current_query_block, 0, false);
227220

228221
int64_t res_size = cfg.query->mem_size() + cfg.target->mem_size(), last_size = 0;
229-
cfg.seed_hit_buf->load(std::min(mem_limit - res_size - cfg.seed_hit_buf->bin_size(1) * (int64_t)sizeof(Search::Hit), config.trace_pt_fetch_size));
222+
cfg.seed_hit_buf->alloc_buffer();
223+
//cfg.seed_hit_buf->load(std::min(mem_limit - res_size - cfg.seed_hit_buf->bin_size(1) * (int64_t)sizeof(Search::Hit), config.trace_pt_fetch_size));
230224

231225
while (true) {
232226
timer.go("Loading trace points");
233-
tuple<vector<Search::Hit>*, BlockId, BlockId> input = cfg.seed_hit_buf->retrieve();
227+
cfg.seed_hit_buf->load(std::min(mem_limit - res_size - cfg.seed_hit_buf->bin_size(1) * (int64_t)sizeof(Search::Hit), config.trace_pt_fetch_size));
228+
tuple<Search::Hit*, int64_t, BlockId, BlockId> input = cfg.seed_hit_buf->retrieve();
234229
if (get<0>(input) == nullptr)
235230
break;
236231
statistics.inc(Statistics::TIME_LOAD_SEED_HITS, timer.microseconds());
237-
vector<Search::Hit>* hit_buf = get<0>(input);
238-
res_size += hit_buf->size() * sizeof(Search::Hit);
239-
query_range = { get<1>(input), get<2>(input) };
240-
cfg.seed_hit_buf->load(std::min(mem_limit - res_size, config.trace_pt_fetch_size));
232+
timer.finish();
233+
Search::Hit* hit_buf = get<0>(input);
234+
const int64_t hit_count = get<1>(input);
235+
log_stream << "Processing " << hit_count << " trace points." << std::endl;
236+
res_size += hit_count * sizeof(Search::Hit);
237+
query_range = { get<2>(input), get<3>(input) };
238+
//cfg.seed_hit_buf->load(std::min(mem_limit - res_size, config.trace_pt_fetch_size));
241239

242240
if (res_size + last_size > mem_limit)
243241
log_stream << "Warning: resident size (" << (res_size + last_size) << ") exceeds memory limit." << std::endl;
244242

245243
timer.go("Sorting trace points");
246244
#ifdef NDEBUG
247-
ips4o::parallel::sort(hit_buf->begin(), hit_buf->end(), std::less<Search::Hit>(), config.threads_);
245+
//sort::sort_parallel_blocked_inplace(hit_buf, hit_buf + hit_count, std::less<Search::Hit>(), config.threads_);
246+
ips4o::parallel::sort(hit_buf, hit_buf + hit_count, std::less<Search::Hit>(), config.threads_);
248247
#else
249-
std::sort(hit_buf->begin(), hit_buf->end());
248+
std::sort(hit_buf, hit_buf + hit_count);
250249
#endif
251250
statistics.inc(Statistics::TIME_SORT_SEED_HITS, timer.microseconds());
252251

253252
timer.go("Computing partition");
254-
const vector<int64_t> partition = make_partition(hit_buf->data(), hit_buf->data() + hit_buf->size());
253+
const vector<int64_t> partition = make_partition(hit_buf, hit_buf + hit_count);
255254

256255
timer.go("Computing alignments");
257-
HitIterator hit_it(query_range.first, query_range.second, hit_buf->data(), hit_buf->data() + hit_buf->size(), partition.begin(), (int64_t)partition.size() - 1);
258-
OutputWriter writer{output_file, cfg.output_format->query_separator};
256+
HitIterator hit_it(query_range.first, query_range.second, hit_buf, hit_buf + hit_count, partition.begin(), (int64_t)partition.size() - 1);
257+
OutputWriter writer{output_file, cfg.blocked_processing ? '\0' : cfg.output_format->query_separator };
259258
output_sink.reset(new ReorderQueue<TextBuffer*, OutputWriter>(query_range.first, writer));
260259
unique_ptr<thread> heartbeat;
261260
if (config.verbosity >= 3 && config.load_balancing == Config::query_parallel && !config.swipe_all && config.heartbeat)
@@ -273,12 +272,13 @@ void align_queries(Consumer* output_file, Search::Config& cfg)
273272
timer.go("Deallocating buffers");
274273
cfg.thread_pool.reset();
275274
output_sink.reset();
276-
last_size = hit_buf->size() * sizeof(Search::Hit);
275+
last_size = hit_count * sizeof(Search::Hit);
277276
res_size -= last_size;
278-
delete hit_buf;
279277
}
280278
statistics.max(Statistics::SEARCH_TEMP_SPACE, cfg.seed_hit_buf->total_disk_size());
281279

280+
timer.go("Freeing memory");
281+
cfg.seed_hit_buf->free_buffer();
282282
if (!cfg.blocked_processing && !cfg.iterated())
283283
cfg.db->end_random_access(false);
284284
}

src/align/alt_hsp.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static TargetVec recompute_alt_hsps(const Sequence* query_seq, const int query_s
6969
const Loc qlen = query_seq[0].length();
7070
for (auto it = targets.begin(); it != targets.end(); ++it) {
7171
const int64_t dp_size = (int64_t)qlen * (int64_t)it->match->seq.length();
72-
const ::Stats::TargetMatrix* matrix = it->match->matrix.blank() ? nullptr : &it->match->matrix;
72+
const ::Stats::TargetMatrix* matrix = it->match->matrix.get();
7373
const int bin = DP::BandedSwipe::bin(v, qlen, 0, 0, dp_size, matrix ? matrix->score_width() : 0, 0);
7474
for (int32_t context = 0; context < align_mode.query_contexts; ++context) {
7575
if (it->masked_seq[context]) {
@@ -81,7 +81,8 @@ static TargetVec recompute_alt_hsps(const Sequence* query_seq, const int query_s
8181

8282
for (int32_t context = 0; context < align_mode.query_contexts; ++context) {
8383
const int8_t* cbs = ::Stats::CBS::hauser(config.comp_based_stats) ? query_cb[context].int8.data() : nullptr;
84-
DP::Params params{ query_seq[context], "", Frame(context), query_source_len, cbs, DP::Flags::FULL_MATRIX, false, 0, v, stats, nullptr };
84+
DP::Params params{ query_seq[context], "", Frame(context), query_source_len, cbs, DP::Flags::FULL_MATRIX, false, 0, -1,
85+
v, stats, nullptr };
8586
list<Hsp> hsp = DP::BandedSwipe::swipe(dp_targets[context], params);
8687
while (!hsp.empty()) {
8788
ActiveTarget& t = targets[hsp.front().swipe_target];

src/align/culling.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,13 @@ bool filter_hsp(Hsp& hsp, int source_query_len, const char *query_title, int sub
156156
const double qcov = hsp.query_cover_percent(source_query_len),
157157
tcov = hsp.subject_cover_percent(subject_len),
158158
approx_min_id = config.approx_min_id.get(0.0);
159+
//const bool filter_uncov = std::max(hsp.query_range.begin_, source_query_len - hsp.query_range.end_) > config.uncov_cap || std::max(hsp.subject_range.begin_, subject_len - hsp.subject_range.end_) > config.uncov_cap;
159160
return !cluster_threshold
160161
|| hsp.id_percent() < config.min_id
161162
|| (approx_min_id > 0 && hsp.approx_id < approx_min_id)
162163
|| qcov < config.query_cover
163164
|| tcov < config.subject_cover
165+
//|| filter_uncov
164166
|| (qcov < config.query_or_target_cover && tcov < config.query_or_target_cover)
165167
|| (config.no_self_hits
166168
&& query_seq == subject_seq

src/align/def.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ HspValues filter_hspvalues();
1515
template<>
1616
struct EnumTraits<Extension::Mode> {
1717
static const SEMap<Extension::Mode> from_string;
18-
};
18+
};

0 commit comments

Comments
 (0)