1
0
mirror of synced 2024-09-24 03:28:24 +02:00

build: Replace old interval tree in favour of custom libwolv one

This commit is contained in:
WerWolv 2023-05-11 09:27:23 +02:00
parent 1d2b8ac1f3
commit 50c3cf8272
18 changed files with 57 additions and 366 deletions

View File

@ -442,14 +442,12 @@ macro(addBundledLibraries)
add_subdirectory(${EXTERN_LIBS_FOLDER}/microtar EXCLUDE_FROM_ALL)
set_target_properties(microtar PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${EXTERN_LIBS_FOLDER}/intervaltree EXCLUDE_FROM_ALL)
set_target_properties(intervaltree PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${EXTERN_LIBS_FOLDER}/libwolv EXCLUDE_FROM_ALL)
set_property(TARGET libwolv-types PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-utils PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-io PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-hash PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-containers PROPERTY POSITION_INDEPENDENT_CODE ON)
set(XDGPP_INCLUDE_DIRS "${EXTERN_LIBS_FOLDER}/xdgpp")

6
dist/rpm/imhex.spec vendored
View File

@ -4,7 +4,7 @@ Release: 0%{?dist}
Summary: A hex editor for reverse engineers and programmers
License: GPL-2.0-only AND Zlib AND MIT AND Apache-2.0
# imhex is gplv2. capstone is custom. nativefiledialog is Zlib. intervaltree is MIT
# imhex is gplv2. capstone is custom. nativefiledialog is Zlib.
# see license dir for full breakdown
URL: https://imhex.werwolv.net/
# We need the archive with deps bundled
@ -36,9 +36,6 @@ Provides: bundled(imgui)
Provides: bundled(libromfs)
Provides: bundled(microtar)
Provides: bundled(libpl)
# ImHex modified upstream intervaltree so we have to package it
# https://github.com/ekg/intervaltree
Provides: bundled(intervaltree) = 0.1
Provides: bundled(xdgpp)
# ftbfs on these arches. armv7hl might compile when capstone 5.x
@ -115,7 +112,6 @@ cp -a lib/external/nativefiledialog/LICENSE %{buildroot}%{
cp -a lib/external/capstone/LICENSE.TXT %{buildroot}%{_datadir}/licenses/%{name}/capstone-LICENSE
cp -a lib/external/capstone/suite/regress/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/capstone-regress-LICENSE
cp -a lib/external/microtar/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/microtar-LICENSE
cp -a lib/external/pattern_language/external/intervaltree/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/pattern-language-intervaltree-LICENSE
cp -a lib/external/xdgpp/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/xdgpp-LICENSE

View File

@ -1,9 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(intervaltree)
set(CMAKE_CXX_STANDARD20)
add_library(intervaltree INTERFACE)
target_include_directories(intervaltree INTERFACE include)
target_compile_options(intervaltree INTERFACE "-DUSE_INTERVAL_TREE_NAMESPACE")

View File

@ -1,23 +0,0 @@
The MIT License
Copyright (c) 2019 Dana-Farber Cancer Institute
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,133 +0,0 @@
## Introduction
cgranges is a small C library for genomic interval overlap queries: given a
genomic region *r* and a set of regions *R*, finding all regions in *R* that
overlaps *r*. Although this library is based on [interval tree][itree], a well
known data structure, the core algorithm of cgranges is distinct from all
existing implementations to the best of our knowledge. Specifically, the
interval tree in cgranges is implicitly encoded as a plain sorted array
(similar to [binary heap][bheap] but packed differently). Tree
traversal is achieved by jumping between array indices. This treatment makes
cgranges very efficient and compact in memory. The core algorithm can be
implemented in ~50 lines of C++ code, much shorter than others as well. Please
see the code comments in [cpp/IITree.h](cpp/IITree.h) for details.
## Usage
### Test with BED coverage
For testing purposes, this repo implements the [bedtools coverage][bedcov] tool
with cgranges. The source code is located in the [test/](test) directory. You
can compile and run the test with:
```sh
cd test && make
./bedcov-cr test1.bed test2.bed
```
The first BED file is loaded into RAM and indexed. The depth and the breadth of
coverage of each region in the second file is computed by query against the
index of the first file.
The [test/](test) directory also contains a few other implementations based on
[IntervalTree.h][ekg-itree] in C++, [quicksect][quicksect] in Cython and
[ncls][ncls] in Cython. The table below shows timing and peak memory on two
test BEDs available in the release page. The first BED contains GenCode
annotations with ~1.2 million lines, mixing all types of features. The second
contains ~10 million direct-RNA mappings. Time1a/Mem1a indexes the GenCode BED
into memory. Time1b adds whole chromosome intervals to the GenCode BED when
indexing. Time2/Mem2 indexes the RNA-mapping BED into memory. Numbers are
averaged over 5 runs.
|Algo. |Lang. |Cov|Program |Time1a|Time1b|Mem1a |Time2 |Mem2 |
|:-------|:-----|:-:|:---------------|-----:|-----:|-------:|-----:|-------:|
|IAITree |C |Y |cgranges |9.0s |13.9s |19.1MB |4.6s |138.4MB |
|IAITree |C++ |Y |cpp/iitree.h |11.1s |24.5s |22.4MB |5.8s |160.4MB |
|CITree |C++ |Y |IntervalTree.h |17.4s |17.4s |27.2MB |10.5s |179.5MB |
|IAITree |C |N |cgranges |7.6s |13.0s |19.1MB |4.1s |138.4MB |
|AIList |C |N |3rd-party/AIList|7.9s |8.1s |14.4MB |6.5s |104.8MB |
|NCList |C |N |3rd-party/NCList|13.0s |13.4s |21.4MB |10.6s |183.0MB |
|AITree |C |N |3rd-party/AITree|16.8s |18.4s |73.4MB |27.3s |546.4MB |
|IAITree |Cython|N |cgranges |56.6s |63.9s |23.4MB |43.9s |143.1MB |
|binning |C++ |Y |bedtools |201.9s|280.4s|478.5MB |149.1s|3438.1MB|
Here, IAITree = implicit augmented interval tree, used by cgranges;
CITree = centered interval tree, used by [Erik Garrison's
IntervalTree][itree]; AIList = augmented interval list, by [Feng et
al][ailist]; NCList = nested containment list, taken from [ncls][ncls] by Feng
et al; AITree = augmented interval tree, from [kerneltree][kerneltree].
"Cov" indicates whether the program calculates breadth of coverage.
Comments:
* AIList keeps start and end only. IAITree and CITree addtionally store a
4-byte "ID" field per interval to reference the source of interval. This is
partly why AIList uses the least memory.
* IAITree is more sensitive to the worse case: the presence of an interval
spanning the whole chromosome.
* IAITree uses an efficient radix sort. CITree uses std::sort from STL, which
is ok. AIList and NCList use qsort from libc, which is slow. Faster sorting
leads to faster indexing.
* IAITree in C++ uses identical core algorithm to the C version, but limited by
its APIs, it wastes time on memory locality and management. CITree has a
similar issue.
* Computing coverage is better done when the returned list of intervals are
start sorted. IAITree returns sorted list. CITree doesn't. Not sure about
others. Computing coverage takes a couple of seconds. Sorting will be slower.
* Printing intervals also takes a noticeable fraction of time. Custom printf
equivalent would be faster.
* IAITree+Cython is a wrapper around the C version of cgranges. Cython adds
significant overhead.
* Bedtools is designed for a variety of applications in addition to computing
coverage. It may keep other information in its internal data structure. This
micro-benchmark may be unfair to bedtools.
* In general, the performance is affected a lot by subtle implementation
details. CITree, IAITree, NCList and AIList are all broadly comparable in
performance. AITree is not recommended when indexed intervals are immutable.
### Use cgranges as a C library
```c
cgranges_t *cr = cr_init(); // initialize a cgranges_t object
cr_add(cr, "chr1", 20, 30, 0); // add a genomic interval
cr_add(cr, "chr2", 10, 30, 1);
cr_add(cr, "chr1", 10, 25, 2);
cr_index(cr); // index
int64_t i, n, *b = 0, max_b = 0;
n = cr_overlap(cr, "chr1", 15, 22, &b, &max_b); // overlap query; output array b[] can be reused
for (i = 0; i < n; ++i) // traverse overlapping intervals
printf("%d\t%d\t%d\n", cr_start(cr, b[i]), cr_end(cr, b[i]), cr_label(cr, b[i]));
free(b); // b[] is allocated by malloc() inside cr_overlap(), so needs to be freed with free()
cr_destroy(cr);
```
### Use IITree as a C++ library
```cpp
IITree<int, int> tree;
tree.add(12, 34, 0); // add an interval
tree.add(0, 23, 1);
tree.add(34, 56, 2);
tree.index(); // index
std::vector<size_t> a;
tree.overlap(22, 25, a); // retrieve overlaps
for (size_t i = 0; i < a.size(); ++i)
printf("%d\t%d\t%d\n", tree.start(a[i]), tree.end(a[i]), tree.data(a[i]));
```
[bedcov]: https://bedtools.readthedocs.io/en/latest/content/tools/coverage.html
[ekg-itree]: https://github.com/ekg/intervaltree
[quicksect]: https://github.com/brentp/quicksect
[ncls]: https://github.com/hunt-genes/ncls
[citree]: https://en.wikipedia.org/wiki/Interval_tree#Centered_interval_tree
[itree]: https://en.wikipedia.org/wiki/Interval_tree
[bheap]: https://en.wikipedia.org/wiki/Binary_heap
[ailist]: https://www.biorxiv.org/content/10.1101/593657v1
[kerneltree]: https://github.com/biocore-ntnu/kerneltree

View File

@ -1,88 +0,0 @@
#pragma once
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdlib>
template<typename S, typename T> // "S" is a scalar type; "T" is the type of data associated with each interval
class IITree {
struct StackCell {
size_t x; // node
int w; // w: 0 if left child hasn't been processed
StackCell() {};
StackCell(size_t x_, int w_) : x(x_), w(w_) {};
};
struct Interval {
S st, en, max;
T data;
Interval() = default;
Interval(const S &s, const S &e, const T &d) : st(s), en(e), max(e), data(d) { }
};
struct IntervalLess {
bool operator()(const Interval &intervalA, const Interval &intervalB) const { return intervalA.st < intervalB.st; }
};
std::vector<Interval> a;
size_t layout_recur(Interval *b, size_t i = 0, size_t k = 0) { // see https://algorithmica.org/en/eytzinger
if (k < a.size()) {
i = layout_recur(b, i, (k<<1) + 1);
b[k] = a[i++];
i = layout_recur(b, i, (k<<1) + 2);
}
return i;
}
void index_BFS(Interval *interval, size_t n) { // set Interval::max
int t = 0;
StackCell stack[64];
stack[t++] = StackCell(0, 0);
while (t) {
StackCell z = stack[--t];
size_t k = z.x, l = k<<1|1, r = l + 1;
if (z.w == 2) { // Interval::max for both children are computed
interval[k].max = interval[k].en;
if (l < n && interval[k].max < interval[l].max) interval[k].max = interval[l].max;
if (r < n && interval[k].max < interval[r].max) interval[k].max = interval[r].max;
} else { // go down into the two children
stack[t++] = StackCell(k, z.w + 1);
if (l + z.w < n)
stack[t++] = StackCell(l + z.w, 0);
}
}
}
public:
void add(const S &s, const S &e, const T &d) { a.push_back(Interval(s, e, d)); }
void index() {
std::sort(a.begin(), a.end(), IntervalLess());
std::vector<Interval> b(a.size());
layout_recur(b.data());
a.clear();
std::copy(b.begin(), b.end(), std::back_inserter(a));
index_BFS(a.data(), a.size());
}
bool overlap(const S &st, const S &en, std::vector<size_t> &out) const {
int t = 0;
std::array<StackCell, 64> stack;
out.clear();
if (a.empty()) return false;
stack[t++] = StackCell(0, 0); // push the root; this is a top down traversal
while (t) { // the following guarantees that numbers in out[] are always sorted
StackCell z = stack[--t];
size_t l = (z.x<<1) + 1, r = l + 1;
if (l >= a.size()) { // a leaf node
if (st < a[z.x].en && a[z.x].st <= en) out.push_back(z.x);
} else if (z.w == 0) { // if left child not processed
stack[t++] = StackCell(z.x, 1); // re-add node z.x, but mark the left child having been processed
if (l < a.size() && a[l].max > st)
stack[t++] = StackCell(l, 0);
} else if (a[z.x].st <= en) { // need to push the right child
if (st < a[z.x].en) out.push_back(z.x); // test if z.x overlaps the query; if yes, append to out[]
if (r < a.size()) stack[t++] = StackCell(r, 0);
}
}
return out.size() > 0? true : false;
}
size_t size(void) const { return a.size(); }
const S &start(size_t i) const { return a[i].st; }
const S &end(size_t i) const { return a[i].en; }
const T &data(size_t i) const { return a[i].data; }
};

@ -1 +1 @@
Subproject commit 2ccf828f79a30c8524ad79bbaea44f33e2d43227
Subproject commit 30c8014d22cebcbb791305b27ff054de4d60e211

@ -1 +1 @@
Subproject commit 4af40da17ac91b43d4f5832dfc47dec7301d2145
Subproject commit 1ace51d331e6e2dca64492a73c20a2a9331b6c9b

View File

@ -69,4 +69,4 @@ elseif (APPLE)
endif ()
target_link_libraries(libimhex PRIVATE ${FMT_LIBRARIES})
target_link_libraries(libimhex PUBLIC dl imgui ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} libpl intervaltree ${MINIAUDIO_LIBRARIES} libwolv-utils libwolv-io libwolv-hash libwolv-net)
target_link_libraries(libimhex PUBLIC dl imgui ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} libpl ${MINIAUDIO_LIBRARIES} libwolv-utils libwolv-io libwolv-hash libwolv-net libwolv-containers)

View File

@ -29,11 +29,7 @@ namespace hex::plugin::builtin {
void readRaw(u64 offset, void *buffer, size_t size) override;
void writeRaw(u64 offset, const void *buffer, size_t size) override;
/**
* @brief closes the file streams used to read the file.
* Need to be called on file write, see https://github.com/WerWolv/ImHex/issues/988
*/
void invalidateFiles();
[[nodiscard]] size_t getActualSize() const override;
void save() override;
@ -60,20 +56,16 @@ namespace hex::plugin::builtin {
[[nodiscard]] std::pair<Region, bool> getRegionValidity(u64 address) const override;
private:
wolv::io::File& getFile();
protected:
std::fs::path m_path;
wolv::io::File m_file;
size_t m_fileSize = 0;
wolv::io::File m_sizeFile;
std::map<std::thread::id, wolv::io::File> m_files;
std::atomic<u32> m_mapCounter = 0;
std::optional<struct stat> m_fileStats;
bool m_readable = false, m_writable = false;
std::mutex m_fileAccessMutex, m_writeMutex;
};
}

View File

@ -2,7 +2,7 @@
#include <hex/providers/provider.hpp>
#include <IITree.h>
#include <wolv/container/interval_tree.hpp>
namespace hex::plugin::builtin {
@ -44,7 +44,7 @@ namespace hex::plugin::builtin {
protected:
bool m_dataValid = false;
size_t m_dataSize = 0x00;
IITree<u64, std::vector<u8>> m_data;
wolv::container::IntervalTree<std::vector<u8>, u64, 0> m_data;
std::fs::path m_sourceFilePath;
};

View File

@ -12,8 +12,6 @@
#include "ui/hex_editor.hpp"
#include <IITree.h>
namespace hex::plugin::builtin {
class ViewDiff : public View {

View File

@ -9,7 +9,7 @@
#include <atomic>
#include <vector>
#include <IITree.h>
#include <wolv/container/interval_tree.hpp>
namespace hex::plugin::builtin {
@ -94,7 +94,7 @@ namespace hex::plugin::builtin {
} m_searchSettings, m_decodeSettings;
using OccurrenceTree = IITree<u64, Occurrence>;
using OccurrenceTree = wolv::container::IntervalTree<Occurrence, u64, 0>;
PerProvider<std::vector<Occurrence>> m_foundOccurrences, m_sortedOccurrences;
PerProvider<OccurrenceTree> m_occurrenceTree;

View File

@ -86,9 +86,6 @@ namespace hex::plugin::builtin {
this->close();
}
);
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
this->close();
}
[[nodiscard]] ImGuiWindowFlags getFlags() const override {

View File

@ -2,7 +2,6 @@
#include <cstring>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization.hpp>
#include <hex/api/project_file_manager.hpp>
@ -60,24 +59,28 @@ namespace hex::plugin::builtin {
if (offset > (this->getActualSize() - size) || buffer == nullptr || size == 0)
return;
auto &file = this->getFile();
file.seek(offset);
file.readBuffer(reinterpret_cast<u8*>(buffer), size);
/*auto currSize = std::fs::file_size(this->m_path);
if (this->m_fileSize != currSize) [[unlikely]] {
this->m_fileSize = currSize;
this->m_file.unmap();
this->m_file.map();
}*/
std::memcpy(buffer, this->m_file.getMapping() + offset, size);
}
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)
return;
std::scoped_lock lock(this->m_writeMutex);
wolv::io::File writeFile(this->m_path, wolv::io::File::Mode::Write);
if (!writeFile.isValid())
return;
writeFile.seek(offset);
writeFile.writeBuffer(reinterpret_cast<const u8*>(buffer), size);
auto currSize = std::fs::file_size(this->m_path);
if (this->m_fileSize != currSize) [[unlikely]] {
this->m_fileSize = currSize;
this->m_file.unmap();
this->m_file.map();
}
this->invalidateFiles();
std::memcpy(this->m_file.getMapping() + offset, buffer, size);
}
void FileProvider::save() {
@ -125,13 +128,6 @@ namespace hex::plugin::builtin {
Provider::insert(offset, size);
}
void FileProvider::invalidateFiles() {
for(auto & [threadId, file] : this->m_files){
file.close();
}
this->m_files.clear();
}
void FileProvider::remove(u64 offset, size_t size) {
auto oldSize = this->getActualSize();
this->resize(oldSize + size);
@ -156,7 +152,7 @@ namespace hex::plugin::builtin {
}
size_t FileProvider::getActualSize() const {
return this->m_sizeFile.getSize();
return this->m_fileSize;
}
std::string FileProvider::getName() const {
@ -220,9 +216,10 @@ namespace hex::plugin::builtin {
}
this->m_fileStats = file.getFileInfo();
this->m_sizeFile = file.clone();
this->m_file = std::move(file);
this->m_files.emplace(std::this_thread::get_id(), std::move(file));
this->m_file.map();
this->m_fileSize = this->m_file.getSize();
return true;
}
@ -231,18 +228,6 @@ namespace hex::plugin::builtin {
}
wolv::io::File& FileProvider::getFile() {
auto threadId = std::this_thread::get_id();
if (!this->m_files.contains(threadId)) {
std::scoped_lock lock(this->m_fileAccessMutex);
if (!this->m_files.contains(threadId))
this->m_files.emplace(threadId, this->m_sizeFile.clone());
}
return this->m_files[threadId];
}
void FileProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);

View File

@ -161,37 +161,25 @@ namespace hex::plugin::builtin {
void IntelHexProvider::setBaseAddress(u64 address) {
auto oldBase = this->getBaseAddress();
std::vector<size_t> indices;
this->m_data.overlap(oldBase, oldBase + this->getActualSize(), indices);
auto regions = this->m_data.overlapping({ oldBase, oldBase + this->getActualSize() });
IITree<u64, std::vector<u8>> intervals;
for (auto &index : indices) {
intervals.add(
(this->m_data.start(index) - oldBase) + address,
(this->m_data.end(index) - oldBase) + address,
this->m_data.data(index)
);
decltype(this->m_data) newIntervals;
for (auto &[interval, data] : regions) {
newIntervals.insert({ interval.start - oldBase + address, interval.end - oldBase + address }, *data);
}
this->m_data = std::move(intervals);
this->m_data.index();
this->m_data = newIntervals;
Provider::setBaseAddress(address);
}
void IntelHexProvider::readRaw(u64 offset, void *buffer, size_t size) {
std::vector<size_t> indices;
this->m_data.overlap(offset, (offset + size) - 1, indices);
auto intervals = this->m_data.overlapping({ offset, (offset + size) - 1 });
std::memset(buffer, 0x00, size);
auto bytes = reinterpret_cast<u8*>(buffer);
for (const auto &index : indices) {
auto start = this->m_data.start(index);
auto end = this->m_data.end(index);
auto data = this->m_data.data(index);
for (u32 i = std::max(start, offset); i <= end && (i - offset) < size; i++) {
bytes[i - offset] = data[i - start];
for (const auto &[interval, data] : intervals) {
for (u32 i = std::max(interval.start, offset); i <= interval.end && (i - offset) < size; i++) {
bytes[i - offset] = (*data)[i - interval.start];
}
}
}
@ -216,12 +204,11 @@ namespace hex::plugin::builtin {
u64 maxAddress = 0x00;
for (auto &[address, bytes] : data) {
auto endAddress = (address + bytes.size()) - 1;
this->m_data.add(address, endAddress, std::move(bytes));
this->m_data.emplace({ address, endAddress }, std::move(bytes));
if (endAddress > maxAddress)
maxAddress = endAddress;
}
this->m_data.index();
this->m_dataSize = maxAddress + 1;
this->m_dataValid = true;
@ -265,22 +252,18 @@ namespace hex::plugin::builtin {
}
std::pair<Region, bool> IntelHexProvider::getRegionValidity(u64 address) const {
std::vector<size_t> indices;
this->m_data.overlap(address, address, indices);
if (indices.empty()) {
auto intervals = this->m_data.overlapping({ address, address });
if (intervals.empty()) {
return Provider::getRegionValidity(address);
}
auto closestIndex = indices.front();
for (const auto &index : indices) {
if (this->m_data.start(index) < this->m_data.start(closestIndex))
closestIndex = index;
decltype(this->m_data)::Interval closestInterval = { 0, 0 };
for (const auto &[interval, data] : intervals) {
if (interval.start < closestInterval.end)
closestInterval = interval;
}
return { Region { closestInterval.start, (closestInterval.end - closestInterval.start) + 1}, true };
auto start = this->m_data.start(closestIndex);
auto end = this->m_data.end(closestIndex);
return { Region { start, (end - start) + 1 }, true };
}
void IntelHexProvider::loadSettings(const nlohmann::json &settings) {

View File

@ -182,12 +182,11 @@ namespace hex::plugin::builtin {
u64 maxAddress = 0x00;
for (auto &[address, bytes] : data) {
auto endAddress = (address + bytes.size()) - 1;
this->m_data.add(address, endAddress, std::move(bytes));
this->m_data.emplace({ address, endAddress }, std::move(bytes));
if (endAddress > maxAddress)
maxAddress = endAddress;
}
this->m_data.index();
this->m_dataSize = maxAddress + 1;
this->m_dataValid = true;

View File

@ -22,8 +22,7 @@ namespace hex::plugin::builtin {
if (this->m_searchTask.isRunning())
return { };
std::vector<size_t> occurrences;
if (this->m_occurrenceTree->overlap(address, address, occurrences))
if (!this->m_occurrenceTree->overlapping({ address, address }).empty())
return HighlightColor();
else
return std::nullopt;
@ -35,8 +34,8 @@ namespace hex::plugin::builtin {
if (this->m_searchTask.isRunning())
return;
std::vector<size_t> occurrences;
if (!this->m_occurrenceTree->overlap(address, address, occurrences))
auto occurrences = this->m_occurrenceTree->overlapping({ address, address });
if (occurrences.empty())
return;
ImGui::BeginTooltip();
@ -48,10 +47,8 @@ namespace hex::plugin::builtin {
ImGui::TableNextColumn();
{
auto start = this->m_occurrenceTree->start(occurrence);
auto end = this->m_occurrenceTree->end(occurrence) - 1;
const auto &bytes = this->m_occurrenceTree->data(occurrence);
const auto value = this->decodeValue(ImHexApi::Provider::get(), bytes, 256);
auto region = occurrence.value.region;
const auto value = this->decodeValue(ImHexApi::Provider::get(), occurrence.value, 256);
ImGui::ColorButton("##color", ImColor(HighlightColor()));
ImGui::SameLine(0, 10);
@ -65,7 +62,7 @@ namespace hex::plugin::builtin {
ImGui::TableNextColumn();
ImGui::TextFormatted("{}: ", "hex.builtin.common.region"_lang);
ImGui::TableNextColumn();
ImGui::TextFormatted("[ 0x{:08X} - 0x{:08X} ]", start, end);
ImGui::TextFormatted("[ 0x{:08X} - 0x{:08X} ]", region.getStartAddress(), region.getEndAddress());
auto demangledValue = llvm::demangle(value);
@ -513,8 +510,7 @@ namespace hex::plugin::builtin {
this->m_sortedOccurrences.get(provider) = this->m_foundOccurrences.get(provider);
for (const auto &occurrence : this->m_foundOccurrences.get(provider))
this->m_occurrenceTree->add(occurrence.region.getStartAddress(), occurrence.region.getEndAddress() + 1, occurrence);
this->m_occurrenceTree->index();
this->m_occurrenceTree->insert({ occurrence.region.getStartAddress(), occurrence.region.getEndAddress() }, occurrence);
});
}