From 60a7d1c3be8f6c7eb697c7d5160fc560097cc70e Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sun, 11 Jan 2026 12:37:46 +0100 Subject: Introduce JSONL performance counter logging Introduce a scoped object that starts measuring performance counters and then dumps them into a JSONL file for later analysis. Add performance contexts for APT::Solver and pkgDepCache::Init() as starting points. --- apt-pkg/contrib/perf.h | 153 +++++++++++++++++++++++++++++++++++++++++++++++++ apt-pkg/depcache.cc | 2 + apt-pkg/edsp.cc | 2 + 3 files changed, 157 insertions(+) create mode 100644 apt-pkg/contrib/perf.h diff --git a/apt-pkg/contrib/perf.h b/apt-pkg/contrib/perf.h new file mode 100644 index 000000000..7a56d5149 --- /dev/null +++ b/apt-pkg/contrib/perf.h @@ -0,0 +1,153 @@ +/* + * Performance measurements + * + * Copyright (C) 2026 Julian Andres Klode + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef APT_PERF_H +#define APT_PERF_H +#if defined(APT_COMPILING_APT) && defined(__linux__) +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace APT +{ + +/** + * \brief A scoped object that will log the performance counters. + * + * Set the "APT_PERFORMANCE_LOG" environment variable to produce a + * JSONL file with records for various contexts, such as the solver. + */ +class PerformanceContext +{ + struct measurement + { + uint32_t type; + uint64_t config; + const char *name; + }; + + static constexpr std::array measurements{ + measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, "instructions"}, + measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, "cpu_cycles"}, + measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, "ref_cpu_cycles"}, + measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, "cache_references"}, + measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, "cache_misses"}, + measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS, "branch_instructions"}, + measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES, "branch_misses"}, + measurement{PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, "cpu_clock"}, + measurement{PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS, "page_faults"}, + measurement{PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS, "cpu_migrations"}, + }; + + /// Output filename + std::string out; + /// Name of the context + std::string name; + /// FDs to communicate with the kernel + std::array fds; + + // Wrapper for the system call + static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, + int cpu, int group_fd, unsigned long flags) + { + return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); + } + + // Wrapper for the system call + static int open_perf_counter(uint32_t type, uint64_t config) + { + struct perf_event_attr pe; + memset(&pe, 0, sizeof(struct perf_event_attr)); + pe.type = type; + pe.size = sizeof(struct perf_event_attr); + pe.config = config; + pe.disabled = 1; + pe.exclude_kernel = 1; + pe.exclude_hv = 1; + + int fd = perf_event_open(&pe, 0, -1, -1, 0); + return fd; + } + + public: + /// Construct a new scoped performance context + PerformanceContext(std::string name) : name(name) + { + if (auto out = getenv("APT_PERFORMANCE_LOG")) + this->out = out; + if (likely(out.empty())) + return; + for (size_t i = 0; i < measurements.size(); ++i) + fds[i] = open_perf_counter(measurements[i].type, measurements[i].config); + for (auto fd : fds) + must_succeed(fd == -1 || ioctl(fd, PERF_EVENT_IOC_RESET, 0) != -1); + for (auto fd : fds) + must_succeed(fd == -1 || ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) != -1); + } + /// Collect the results and store them in the specified performance file + ~PerformanceContext() + { + if (likely(out.empty())) + return; + for (auto fd : fds) + must_succeed(fd == -1 || ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) != -1); + + std::array values; + for (size_t i = 0; i < measurements.size(); ++i) + must_succeed(fds[i] == -1 || read(fds[i], &values[i], sizeof(values[i])) == sizeof(values[i])); + for (auto fd : fds) + must_succeed(fd == -1 || close(fd) == 0); + + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << "{\"context\": " << '"' << name << '"'; + for (size_t i = 0; i < measurements.size(); ++i) + { + ss << ", "; + ss << '"' << measurements[i].name << '"' << ": " << values[i]; + } + + ss << "}\n"; + + std::string entry = ss.str(); + + // Atomically append a line to the JSONL file, allowing all users to read it + int fd = open(out.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); + must_succeed(fd != -1); + must_succeed(flock(fd, LOCK_EX) == 0); + must_succeed(write(fd, entry.c_str(), entry.size()) == static_cast(entry.size())); + must_succeed(flock(fd, LOCK_UN) == 0); + must_succeed(close(fd) == 0); + } +}; + +} // namespace APT + +#else +namespace APT +{ +struct PerformanceContext +{ + PerformanceContext(const char *) {}; + ~PerformanceContext() {}; +}; +} // namespace APT +#endif +#endif diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index f7f508f85..2a9ef50d2 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -225,6 +226,7 @@ bool pkgDepCache::CheckConsistency(char const *const msgtag) /*{{{*/ /* This allocats the extension buffers and initializes them. */ bool pkgDepCache::Init(OpProgress * const Prog) { + APT::PerformanceContext perf{"pkgDepCache::Init"}; // Suppress mark updates during this operation (just in case) and // run a mark operation when Init terminates. ActionGroup actions(*this); diff --git a/apt-pkg/edsp.cc b/apt-pkg/edsp.cc index 2d7fdcc3c..5a9808ff4 100644 --- a/apt-pkg/edsp.cc +++ b/apt-pkg/edsp.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -796,6 +797,7 @@ bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache, unsigned int const flags, OpProgress *Progress) { if (strstr(solver, "3.") == solver) { + APT::PerformanceContext context{"APT::Solver"}; APT::Solver::DependencySolver s(Cache.GetCache(), Cache.GetPolicy(), (EDSP::Request::Flags)flags); FileFd output; bool res = true; -- cgit v1.2.3-70-g09d2 From 5cbf75ac2e4845b9ed1026f1d11af129e54aaf5b Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 31 Jan 2026 21:04:02 +0100 Subject: solver3: Remove dead code It used to be that we reached conflict clauses from the Solve() loop, however that is no longer the case, so remove the else branch, and turn the `else if (item.clause->optional)` into a new `else` with an `assert(item.clause->optional)`. --- apt-pkg/solver3.cc | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index e4449339e..ca32cd1d4 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -596,20 +596,11 @@ bool Solver::Solve() << "(try it: " << candidate->toString(cache) << ")\n"; must_succeed(Assume(*candidate, item.clause)); } - else if (item.clause->optional) - { - if (unlikely(debug >= 1)) - std::cerr << item.toString(cache) << "\n"; - } else { - abort(); + assert(item.clause->optional); if (unlikely(debug >= 1)) std::cerr << item.toString(cache) << "\n"; - // Enqueue produces the right error message for us here, given that reason has been assigned true already... - assert(value(item.clause->reason) == LiftedBool::True); - must_succeed(not Enqueue(~item.clause->reason, item.clause)); - assert(value(item.clause->reason) == LiftedBool::True); } // Must push to trail after any Assume() above. trail.push_back(Trail{Var(), item}); -- cgit v1.2.3-70-g09d2 From 7d5ec84c13227290cfb1bb83d27ae354200bcb02 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 31 Jan 2026 21:59:32 +0100 Subject: solver3: Refactor Propagate() using lazy head/tail Calculate the head and the tail of the clause in Propagate() and check based on that if the clause is conflict/unit/undecided. Special care has been taken to avoid the calculation of tail when it is not necessary by placing it inside a helper lambda; as well as skipping the calculation when the clause is inactive. --- apt-pkg/solver3.cc | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index ca32cd1d4..ed2382432 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -420,37 +420,31 @@ bool Solver::Propagate(const Clause *clause, Lit p) } // Check if the clause is unit, conflict, or undecided - Lit unit; - for (auto sol : clause->solutions) + auto not_false = [&](auto lit) { return value(lit) != LiftedBool::False; }; + auto head = std::ranges::find_if(clause->solutions, not_false); + auto find_tail = [&]() { return std::ranges::find_last_if(clause->solutions, not_false); }; + if (head == clause->solutions.end()) { - if (value(sol) == LiftedBool::False) - continue; - if (not unit.empty() || clause->optional) - { - // We found a second solution, so we are undecided - if (p == clause->reason) - return AddWork(Work{clause, decisionLevel()}); - return true; - } - - unit = sol; + // Conflict clause. If this clause is non-optional; reject its "reason". + if (not clause->optional) + return Enqueue(~clause->reason, clause); } - - // The clause is now either unit or conflict - if (not unit.empty()) + else if (value(clause->reason) != LiftedBool::True) { - // Unit clause. If it is an active clause, enqueue the unit literal. - if (value(clause->reason) == LiftedBool::True) - return Enqueue(unit, clause); - return true; + // Inactive clause, nothing to do + } + else if (not clause->optional && head == find_tail().begin()) + { + // Unit clause. Enqueue the only possible solution + return Enqueue(*head, clause); } else { - // Conflict clause. If this clause is non-optional; reject it's "reason". - if (not clause->optional) - return Enqueue(~clause->reason, clause); - return true; + // Undecided clause. If this is an activation, queue it for solving + if (p == clause->reason) + return AddWork(Work{clause, decisionLevel()}); } + return true; } void Solver::UndoOne() -- cgit v1.2.3-70-g09d2 From 7d84e1f830727ca5008566113e7dfd35df049bd6 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 31 Jan 2026 22:33:40 +0100 Subject: solver3: Fix off-by-one missing optimization We were trying to compare the dependencies in the first version with the dependencies in later version, but our loop started at the first version as well due to an oversight in the use of the increment operator. Change the increment from postfix to prefix such that we start iterating with the 2nd version in the list only. This should yield minute performance optimizations: before: 12: 17.80% 0.00% apt libapt-pkg.so.7.0.0 [.] EDSP::ResolveExternal(char const*, pkgDepCache&, unsigned int, OpProgress*) 14: --17.80%--EDSP::ResolveExternal(char const*, pkgDepCache&, unsigned int, OpProgress*) 20: | | |--6.34%--APT::Solver::DependencySolver::RegisterCommonDependencies(pkgCache::PkgIterator) 44: | | --0.85%--APT::Solver::DependencySolver::RegisterCommonDependencies(pkgCache::PkgIterator) after: 12: 16.98% 0.00% apt libapt-pkg.so.7.0.0 [.] EDSP::ResolveExternal(char const*, pkgDepCache&, unsigned int, OpProgress*) 14: --16.98%--EDSP::ResolveExternal(char const*, pkgDepCache&, unsigned int, OpProgress*) 20: | | |--5.65%--APT::Solver::DependencySolver::RegisterCommonDependencies(pkgCache::PkgIterator) 42: | | --0.70%--APT::Solver::DependencySolver::RegisterCommonDependencies(pkgCache::PkgIterator) --- apt-pkg/solver3.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index ed2382432..2727c3a1d 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -1008,7 +1008,7 @@ void DependencySolver::RegisterCommonDependencies(pkgCache::PkgIterator Pkg) dep.GlobOr(start, end); // advances dep bool allHaveDep = true; - for (auto ver = Pkg.VersionList()++; allHaveDep && not ver.end(); ver++) + for (auto ver = ++Pkg.VersionList(); allHaveDep && not ver.end(); ver++) { bool haveDep = false; for (auto otherDep = ver.DependsList(); not haveDep && not otherDep.end();) -- cgit v1.2.3-70-g09d2 From e7c1adda00abd16285f5eb53c48587855f2a2992 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sat, 31 Jan 2026 22:41:04 +0100 Subject: solver3: Avoid manual delete[] in favor of RAII --- apt-pkg/solver3.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index 2727c3a1d..326e43529 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -1053,9 +1053,9 @@ Clause DependencySolver::TranslateOrGroup(pkgCache::DepIterator start, pkgCache: } else { - auto all = start.AllTargets(); + std::unique_ptr all(start.AllTargets()); - for (auto tgt = all; *tgt; ++tgt) + for (auto tgt = all.get(); *tgt; ++tgt) { pkgCache::VerIterator tgti(cache, *tgt); @@ -1063,7 +1063,6 @@ Clause DependencySolver::TranslateOrGroup(pkgCache::DepIterator start, pkgCache: std::cerr << "Adding work to item " << reason.toString(cache) << " -> " << tgti.ParentPkg().FullName() << "=" << tgti.VerStr() << (clause.negative ? " (negative)" : "") << "\n"; clause.solutions.push_back(Var(pkgCache::VerIterator(cache, *tgt))); } - delete[] all; std::stable_sort(clause.solutions.begin() + begin, clause.solutions.end(), CompareProviders3{cache, policy, start.TargetPkg(), *this}); } if (start == end) -- cgit v1.2.3-70-g09d2 From 8e1750a22ef8be521bdb0542a78438f8d8abc54e Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sun, 1 Feb 2026 09:25:31 +0100 Subject: solver3: Minor style refactorings The for loop with if(foo) continue; return false; was highly unusual as were the bunch of uses of `!` instead of `not`. Gbp-dch: ignore --- apt-pkg/solver3.cc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index 326e43529..d8d350dc5 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -87,7 +87,7 @@ std::string APT::Solver::Clause::toString(pkgCache &cache, bool pretty, bool sho out.append(dep.TargetPkg().FullName(true)); if (dep.TargetVer()) out.append(" (").append(dep.CompType()).append(" ").append(dep.TargetVer()).append(")"); - if (!(dep->CompareOp & pkgCache::Dep::Or)) + if (not(dep->CompareOp & pkgCache::Dep::Or)) break; out.append(" | "); } @@ -376,7 +376,7 @@ bool Solver::Enqueue(Lit lit, const Clause *reason) bool Solver::Propagate() { - while (!propQ.empty()) + while (not propQ.empty()) { Var var = propQ.front(); propQ.pop(); @@ -391,11 +391,8 @@ bool Solver::Propagate() Discover(lit.var()); for (auto clause : watches(lit)) - { - if (Propagate(clause, lit)) - continue; - return false; - } + if (not Propagate(clause, lit)) + return false; } return true; } -- cgit v1.2.3-70-g09d2 From 27e845d53da7e1794eadeb93f4b6f591042ad20c Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Sun, 1 Feb 2026 09:31:10 +0100 Subject: solver3: Mark DependencySolver as final --- apt-pkg/solver3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h index af1d749eb..fc2e90b7f 100644 --- a/apt-pkg/solver3.h +++ b/apt-pkg/solver3.h @@ -425,7 +425,7 @@ class Solver * It is a brute force solver with heuristics, conflicts learning, and 2**32 levels * of backtracking. */ -class DependencySolver : public Solver +class DependencySolver final : public Solver { friend class CompareProviders3; -- cgit v1.2.3-70-g09d2 From a08f18e2a1b7e409d2d20d55749d378d4c21daa6 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Mon, 9 Feb 2026 11:42:26 +0100 Subject: solver3: Cache straight pointers in candidate cache This reduces its memory usage by half and turns it into a fast map - no destructors needed (and 0 initialization). --- apt-pkg/solver3.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h index fc2e90b7f..4390d589b 100644 --- a/apt-pkg/solver3.h +++ b/apt-pkg/solver3.h @@ -461,12 +461,12 @@ class DependencySolver final : public Solver } // GetCandidateVer() with caching - mutable ContiguousCacheMap candidates; + mutable FastContiguousCacheMap candidates; pkgCache::VerIterator GetCandidateVer(pkgCache::PkgIterator pkg) const { - if (candidates[pkg].end()) + if (candidates[pkg] == 0) candidates[pkg] = policy.GetCandidateVer(pkg); - return candidates[pkg]; + return pkgCache::VerIterator(cache, candidates[pkg]); } // \brief Discover variables -- cgit v1.2.3-70-g09d2 From 2e4cc2aebecac327c758965667643d36a02889e5 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Mon, 9 Feb 2026 11:40:49 +0100 Subject: pkgcache: Change inline to constexpr noexcept In particular, map_pointer and iterators are now all literal types, with most functions translated to constexpr noexcept. This allows them to be used in constexpr contexts in C++17; and the compiler to generate better code knowing they cannot throw exceptions. This transformation is safe, because the functions are inline. more constexpr --- apt-pkg/cacheiterators.h | 268 +++++++++++++++++++++++------------------------ apt-pkg/pkgcache.h | 66 ++++++------ 2 files changed, 167 insertions(+), 167 deletions(-) diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h index cc2179bf7..059dc1366 100644 --- a/apt-pkg/cacheiterators.h +++ b/apt-pkg/cacheiterators.h @@ -53,7 +53,7 @@ template class APT_PUBLIC pkgCache::Iterator { * basic methods from the actual structure. * \return Pointer to the first structure of this type */ - Str* OwnerPointer() const { return static_cast(this)->OwnerPointer(); } + constexpr Str* OwnerPointer() const noexcept { return static_cast(this)->OwnerPointer(); } protected: Str *S; @@ -67,25 +67,25 @@ template class APT_PUBLIC pkgCache::Iterator { using pointer = Str*; using reference = Str&; // Iteration - inline bool end() const {return Owner == 0 || S == OwnerPointer();} + constexpr bool end() const noexcept {return Owner == 0 || S == OwnerPointer();} // Comparison - inline bool operator ==(const Itr &B) const {return S == B.S;} - inline bool operator !=(const Itr &B) const {return S != B.S;} + constexpr bool operator ==(const Itr &B) const noexcept {return S == B.S;} + constexpr bool operator !=(const Itr &B) const noexcept {return S != B.S;} // Accessors - inline Str *operator ->() {return S;} - inline Str const *operator ->() const {return S;} - inline operator Str *() {return S == OwnerPointer() ? 0 : S;} - inline operator Str const *() const {return S == OwnerPointer() ? 0 : S;} - inline Str &operator *() {return *S;} - inline Str const &operator *() const {return *S;} - inline pkgCache *Cache() const {return Owner;} + constexpr Str *operator ->() noexcept {return S;} + constexpr Str const *operator ->() const noexcept {return S;} + constexpr operator Str *() noexcept {return S == OwnerPointer() ? 0 : S;} + constexpr operator Str const *() const noexcept {return S == OwnerPointer() ? 0 : S;} + constexpr Str &operator *() noexcept {return *S;} + constexpr Str const &operator *() const noexcept {return *S;} + constexpr pkgCache *Cache() const noexcept {return Owner;} // Mixed stuff - inline bool IsGood() const { return S && Owner && ! end();} - inline unsigned long Index() const {return S - OwnerPointer();} - inline map_pointer MapPointer() const {return map_pointer(Index()) ;} + constexpr bool IsGood() const noexcept { return S && Owner && ! end();} + constexpr unsigned long Index() const noexcept {return S - OwnerPointer();} + constexpr map_pointer MapPointer() const noexcept {return map_pointer(Index()) ;} void ReMap(void const * const oldMap, void * const newMap) { if (Owner == 0 || S == 0) @@ -94,8 +94,8 @@ template class APT_PUBLIC pkgCache::Iterator { } // Constructors - look out for the variable assigning - inline Iterator() : S(0), Owner(0) {} - inline Iterator(pkgCache &Owner,Str *T = 0) : S(T), Owner(&Owner) {} + constexpr Iterator() noexcept : S(0), Owner(0) {} + constexpr Iterator(pkgCache &Owner,Str *T = 0) noexcept : S(T), Owner(&Owner) {} }; /*}}}*/ // Group Iterator /*{{{*/ @@ -107,12 +107,12 @@ class APT_PUBLIC pkgCache::GrpIterator: public Iterator { long HashIndex; public: - inline Group* OwnerPointer() const { + constexpr Group* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->GrpP : 0; } // This constructor is the 'begin' constructor, never use it. - explicit inline GrpIterator(pkgCache &Owner) : Iterator(Owner), HashIndex(-1) { + explicit inline GrpIterator(pkgCache &Owner) noexcept : Iterator(Owner), HashIndex(-1) { S = OwnerPointer(); operator++(); } @@ -120,9 +120,9 @@ class APT_PUBLIC pkgCache::GrpIterator: public Iterator { GrpIterator& operator++(); inline GrpIterator operator++(int) { GrpIterator const tmp(*this); operator++(); return tmp; } - inline const char *Name() const {return S->Name == 0?0:Owner->StrP + S->Name;} - inline PkgIterator PackageList() const; - inline VerIterator VersionsInSource() const; + constexpr const char *Name() const noexcept {return S->Name == 0?0:Owner->StrP + S->Name;} + constexpr PkgIterator PackageList() const noexcept; + constexpr VerIterator VersionsInSource() const noexcept; PkgIterator FindPkg(std::string_view Arch = {"any", 3}) const; /** \brief find the package with the "best" architecture @@ -135,11 +135,11 @@ class APT_PUBLIC pkgCache::GrpIterator: public Iterator { PkgIterator NextPkg(PkgIterator const &Pkg) const; // Constructors - inline GrpIterator(pkgCache &Owner, Group *Trg) : Iterator(Owner, Trg), HashIndex(0) { + constexpr GrpIterator(pkgCache &Owner, Group *Trg) noexcept : Iterator(Owner, Trg), HashIndex(0) { if (S == 0) S = OwnerPointer(); } - inline GrpIterator() : Iterator(), HashIndex(0) {} + constexpr GrpIterator() noexcept : Iterator(), HashIndex(0) {} }; /*}}}*/ @@ -148,12 +148,12 @@ class APT_PUBLIC pkgCache::PkgIterator: public Iterator { long HashIndex; public: - inline Package* OwnerPointer() const { + inline Package* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->PkgP : 0; } // This constructor is the 'begin' constructor, never use it. - explicit inline PkgIterator(pkgCache &Owner) : Iterator(Owner), HashIndex(-1) { + explicit inline PkgIterator(pkgCache &Owner) noexcept : Iterator(Owner), HashIndex(-1) { S = OwnerPointer(); operator++(); } @@ -164,16 +164,16 @@ class APT_PUBLIC pkgCache::PkgIterator: public Iterator { enum OkState {NeedsNothing,NeedsUnpack,NeedsConfigure}; // Accessors - inline const char *Name() const { return Group().Name(); } - inline bool Purge() const {return S->CurrentState == pkgCache::State::Purge || + constexpr const char *Name() const noexcept { return Group().Name(); } + constexpr bool Purge() const noexcept {return S->CurrentState == pkgCache::State::Purge || (S->CurrentVer == 0 && S->CurrentState == pkgCache::State::NotInstalled);} - inline const char *Arch() const {return S->Arch == 0?0:Owner->StrP + S->Arch;} - inline APT_PURE GrpIterator Group() const { return GrpIterator(*Owner, Owner->GrpP + S->Group);} + constexpr const char *Arch() const noexcept {return S->Arch == 0?0:Owner->StrP + S->Arch;} + constexpr APT_PURE GrpIterator Group() const noexcept { return GrpIterator(*Owner, Owner->GrpP + S->Group);} - inline VerIterator VersionList() const APT_PURE; - inline VerIterator CurrentVer() const APT_PURE; - inline DepIterator RevDependsList() const APT_PURE; - inline PrvIterator ProvidesList() const APT_PURE; + constexpr VerIterator VersionList() const noexcept APT_PURE; + constexpr VerIterator CurrentVer() const noexcept APT_PURE; + constexpr DepIterator RevDependsList() const noexcept APT_PURE; + constexpr PrvIterator ProvidesList() const noexcept APT_PURE; OkState State() const APT_PURE; const char *CurVersion() const APT_PURE; @@ -182,18 +182,18 @@ class APT_PUBLIC pkgCache::PkgIterator: public Iterator { std::string FullName(bool const &Pretty = false) const; // Constructors - inline PkgIterator(pkgCache &Owner,Package *Trg) : Iterator(Owner, Trg), HashIndex(0) { + constexpr PkgIterator(pkgCache &Owner,Package *Trg) noexcept : Iterator(Owner, Trg), HashIndex(0) { if (S == 0) S = OwnerPointer(); } - inline PkgIterator() : Iterator(), HashIndex(0) {} + constexpr PkgIterator() noexcept : Iterator(), HashIndex(0) {} }; /*}}}*/ // SourceVersion Iterator /*{{{*/ class APT_PUBLIC pkgCache::SrcVerIterator : public Iterator { public: - inline SourceVersion *OwnerPointer() const + constexpr SourceVersion *OwnerPointer() const noexcept { return (Owner != 0) ? Owner->SrcVerP : 0; } @@ -202,22 +202,22 @@ class APT_PUBLIC pkgCache::SrcVerIterator : public IteratorSrcVerP) S = Owner->SrcVerP + S->NextSourceVersion; return *this;} inline SrcVerIterator operator++(int) { SrcVerIterator const tmp(*this); operator++(); return tmp; } #endif - inline APT_PURE GrpIterator Group() const { return GrpIterator(*Owner, Owner->GrpP + S->Group); } - inline const char *VerStr() const { return S->VerStr == 0 ? 0 : Owner->StrP + S->VerStr; } + constexpr APT_PURE GrpIterator Group() const noexcept { return GrpIterator(*Owner, Owner->GrpP + S->Group); } + constexpr const char *VerStr() const noexcept { return S->VerStr == 0 ? 0 : Owner->StrP + S->VerStr; } - inline SrcVerIterator(pkgCache &Owner, SourceVersion *Trg = 0) : Iterator(Owner, Trg) + constexpr SrcVerIterator(pkgCache &Owner, SourceVersion *Trg = 0) noexcept : Iterator(Owner, Trg) { if (S == 0) S = OwnerPointer(); } - inline SrcVerIterator() : Iterator() {} + constexpr SrcVerIterator() noexcept : Iterator() {} }; /*}}}*/ // Version Iterator /*{{{*/ class APT_PUBLIC pkgCache::VerIterator : public Iterator { public: - inline Version* OwnerPointer() const { + inline Version* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->VerP : 0; } @@ -225,7 +225,7 @@ class APT_PUBLIC pkgCache::VerIterator : public Iterator { inline VerIterator& operator++() {if (S != Owner->VerP) S = Owner->VerP + S->NextVer; return *this;} inline VerIterator operator++(int) { VerIterator const tmp(*this); operator++(); return tmp; } - inline VerIterator NextInSource() + constexpr VerIterator NextInSource() noexcept { if (S != Owner->VerP) S = Owner->VerP + S->NextInSource; @@ -238,35 +238,35 @@ class APT_PUBLIC pkgCache::VerIterator : public Iterator { This method should be used to identify if two pseudo versions are referring to the same "real" version */ - inline bool SimilarVer(const VerIterator &B) const { + constexpr bool SimilarVer(const VerIterator &B) const noexcept { return (B.end() == false && S->Hash == B->Hash && strcmp(VerStr(), B.VerStr()) == 0); } // Accessors - inline const char *VerStr() const {return S->VerStr == 0?0:Owner->StrP + S->VerStr;} - inline const char *Section() const {return S->Section == 0?0:Owner->StrP + S->Section;} + constexpr const char *VerStr() const noexcept {return S->VerStr == 0?0:Owner->StrP + S->VerStr;} + constexpr const char *Section() const noexcept {return S->Section == 0?0:Owner->StrP + S->Section;} /** \brief source version this version comes from Always contains the version string, even if it is the same as the binary version */ - SrcVerIterator SourceVersion() const { return SrcVerIterator(*Owner, Owner->SrcVerP + S->SourceVersion); } + constexpr SrcVerIterator SourceVersion() const noexcept { return SrcVerIterator(*Owner, Owner->SrcVerP + S->SourceVersion); } /** \brief source package name this version comes from Always contains the name, even if it is the same as the binary name */ - inline const char *SourcePkgName() const { return SourceVersion().Group().Name(); } + constexpr const char *SourcePkgName() const noexcept { return SourceVersion().Group().Name(); } /** \brief source version this version comes from Always contains the version string, even if it is the same as the binary version */ - inline const char *SourceVerStr() const { return SourceVersion().VerStr(); } - inline const char *Arch() const { + constexpr const char *SourceVerStr() const noexcept { return SourceVersion().VerStr(); } + constexpr const char *Arch() const noexcept { if ((S->MultiArch & pkgCache::Version::All) == pkgCache::Version::All) return "all"; return S->ParentPkg == 0?0:Owner->StrP + ParentPkg()->Arch; } - inline PkgIterator ParentPkg() const {return PkgIterator(*Owner,Owner->PkgP + S->ParentPkg);} + constexpr PkgIterator ParentPkg() const noexcept {return PkgIterator(*Owner,Owner->PkgP + S->ParentPkg);} - inline DescIterator DescriptionList() const; + constexpr DescIterator DescriptionList() const noexcept; DescIterator TranslatedDescriptionForLanguage(std::string_view lang) const; DescIterator TranslatedDescription() const; - inline DepIterator DependsList() const; - inline PrvIterator ProvidesList() const; - inline VerFileIterator FileList() const; + constexpr DepIterator DependsList() const noexcept; + constexpr PrvIterator ProvidesList() const noexcept; + constexpr VerFileIterator FileList() const noexcept; bool Downloadable() const; inline const char *PriorityType() const {return Owner->Priority(S->Priority);} const char *MultiArchType() const APT_PURE; @@ -300,17 +300,17 @@ class APT_PUBLIC pkgCache::VerIterator : public Iterator { } #endif - inline VerIterator(pkgCache &Owner,Version *Trg = 0) : Iterator(Owner, Trg) { + constexpr VerIterator(pkgCache &Owner,Version *Trg = 0) noexcept : Iterator(Owner, Trg) { if (S == 0) S = OwnerPointer(); } - inline VerIterator() : Iterator() {} + constexpr VerIterator() noexcept : Iterator() {} }; /*}}}*/ // Description Iterator /*{{{*/ class APT_PUBLIC pkgCache::DescIterator : public Iterator { public: - inline Description* OwnerPointer() const { + constexpr Description* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->DescP : 0; } @@ -322,12 +322,12 @@ class APT_PUBLIC pkgCache::DescIterator : public IteratorStrP + S->language_code;} - inline const char *md5() const {return Owner->StrP + S->md5sum;} - inline DescFileIterator FileList() const; + constexpr const char *LanguageCode() const noexcept {return Owner->StrP + S->language_code;} + constexpr const char *md5() const noexcept {return Owner->StrP + S->md5sum;} + constexpr DescFileIterator FileList() const noexcept; - inline DescIterator() : Iterator() {} - inline DescIterator(pkgCache &Owner,Description *Trg = 0) : Iterator(Owner, Trg) { + constexpr DescIterator() noexcept : Iterator() {} + constexpr DescIterator(pkgCache &Owner,Description *Trg = 0) noexcept : Iterator(Owner, Trg) { if (S == 0) S = Owner.DescP; } @@ -339,7 +339,7 @@ class APT_PUBLIC pkgCache::DepIterator : public IteratorDepP : 0; } @@ -348,12 +348,12 @@ class APT_PUBLIC pkgCache::DepIterator : public IteratorVersion == 0?0:Owner->StrP + S2->Version;} - inline PkgIterator TargetPkg() const {return PkgIterator(*Owner,Owner->PkgP + S2->Package);} - inline PkgIterator SmartTargetPkg() const {PkgIterator R(*Owner,0);SmartTargetPkg(R);return R;} - inline VerIterator ParentVer() const {return VerIterator(*Owner,Owner->VerP + S->ParentVer);} - inline PkgIterator ParentPkg() const {return PkgIterator(*Owner,Owner->PkgP + Owner->VerP[uint32_t(S->ParentVer)].ParentPkg);} - inline bool Reverse() const {return Type == DepRev;} + constexpr const char *TargetVer() const noexcept {return S2->Version == 0?0:Owner->StrP + S2->Version;} + constexpr PkgIterator TargetPkg() const noexcept {return PkgIterator(*Owner,Owner->PkgP + S2->Package);} + inline PkgIterator SmartTargetPkg() const noexcept {PkgIterator R(*Owner,0);SmartTargetPkg(R);return R;} + constexpr VerIterator ParentVer() const noexcept {return VerIterator(*Owner,Owner->VerP + S->ParentVer);} + constexpr PkgIterator ParentPkg() const noexcept {return PkgIterator(*Owner,Owner->PkgP + Owner->VerP[uint32_t(S->ParentVer)].ParentPkg);} + constexpr bool Reverse() const noexcept {return Type == DepRev;} bool IsCritical() const APT_PURE; bool IsNegative() const APT_PURE; bool IsIgnorable(PrvIterator const &Prv) const APT_PURE; @@ -361,7 +361,7 @@ class APT_PUBLIC pkgCache::DepIterator : public IteratorCompareOp & pkgCache::Dep::MultiArchImplicit) == pkgCache::Dep::MultiArchImplicit; } /* This covers additionally negative dependencies, which aren't arch-specific, @@ -390,11 +390,11 @@ class APT_PUBLIC pkgCache::DepIterator : public Iterator &NextRevDepends; map_pointer &NextDepends; map_pointer &NextData; - DependencyProxy const * operator->() const { return this; } - DependencyProxy * operator->() { return this; } + constexpr DependencyProxy const * operator->() const noexcept { return this; } + constexpr DependencyProxy * operator->() noexcept { return this; } }; - inline DependencyProxy operator->() const {return (DependencyProxy) { S2->Version, S2->Package, S->ID, S2->Type, S2->CompareOp, S->ParentVer, S->DependencyData, S->NextRevDepends, S->NextDepends, S2->NextData };} - inline DependencyProxy operator->() {return (DependencyProxy) { S2->Version, S2->Package, S->ID, S2->Type, S2->CompareOp, S->ParentVer, S->DependencyData, S->NextRevDepends, S->NextDepends, S2->NextData };} + constexpr DependencyProxy operator->() const noexcept {return (DependencyProxy) { S2->Version, S2->Package, S->ID, S2->Type, S2->CompareOp, S->ParentVer, S->DependencyData, S->NextRevDepends, S->NextDepends, S2->NextData };} + constexpr DependencyProxy operator->() {return (DependencyProxy) { S2->Version, S2->Package, S->ID, S2->Type, S2->CompareOp, S->ParentVer, S->DependencyData, S->NextRevDepends, S->NextDepends, S2->NextData };} void ReMap(void const * const oldMap, void * const newMap) { Iterator::ReMap(oldMap, newMap); @@ -406,17 +406,17 @@ class APT_PUBLIC pkgCache::DepIterator : public Iterator(Owner, Trg), Type(DepVer), S2(Trg == 0 ? Owner.DepDataP : (Owner.DepDataP + Trg->DependencyData)) { if (S == 0) S = Owner.DepP; } - inline DepIterator(pkgCache &Owner, Dependency *Trg, Package*) : + constexpr DepIterator(pkgCache &Owner, Dependency *Trg, Package*) noexcept : Iterator(Owner, Trg), Type(DepRev), S2(Trg == 0 ? Owner.DepDataP : (Owner.DepDataP + Trg->DependencyData)) { if (S == 0) S = Owner.DepP; } - inline DepIterator() : Iterator(), Type(DepVer), S2(0) {} + constexpr DepIterator() noexcept : Iterator(), Type(DepVer), S2(0) {} }; /*}}}*/ // Provides iterator /*{{{*/ @@ -424,7 +424,7 @@ class APT_PUBLIC pkgCache::PrvIterator : public Iterator enum {PrvVer, PrvPkg} Type; public: - inline Provides* OwnerPointer() const { + constexpr Provides* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->ProvideP : 0; } @@ -437,11 +437,11 @@ class APT_PUBLIC pkgCache::PrvIterator : public Iterator inline PrvIterator operator++(int) { PrvIterator const tmp(*this); operator++(); return tmp; } // Accessors - inline const char *Name() const {return ParentPkg().Name();} - inline const char *ProvideVersion() const {return S->ProvideVersion == 0?0:Owner->StrP + S->ProvideVersion;} - inline PkgIterator ParentPkg() const {return PkgIterator(*Owner,Owner->PkgP + S->ParentPkg);} - inline VerIterator OwnerVer() const {return VerIterator(*Owner,Owner->VerP + S->Version);} - inline PkgIterator OwnerPkg() const {return PkgIterator(*Owner,Owner->PkgP + Owner->VerP[uint32_t(S->Version)].ParentPkg);} + constexpr const char *Name() const noexcept {return ParentPkg().Name();} + constexpr const char *ProvideVersion() const noexcept {return S->ProvideVersion == 0?0:Owner->StrP + S->ProvideVersion;} + constexpr PkgIterator ParentPkg() const noexcept {return PkgIterator(*Owner,Owner->PkgP + S->ParentPkg);} + constexpr VerIterator OwnerVer() const noexcept {return VerIterator(*Owner,Owner->VerP + S->Version);} + constexpr PkgIterator OwnerPkg() const noexcept {return PkgIterator(*Owner,Owner->PkgP + Owner->VerP[uint32_t(S->Version)].ParentPkg);} /* MultiArch can be translated to SingleArch for an resolver and we did so, by adding provides to help the resolver understand the problem, but @@ -450,13 +450,13 @@ class APT_PUBLIC pkgCache::PrvIterator : public Iterator { return (S->Flags & pkgCache::Flag::MultiArchImplicit) == pkgCache::Flag::MultiArchImplicit; } - inline PrvIterator() : Iterator(), Type(PrvVer) {} - inline PrvIterator(pkgCache &Owner, Provides *Trg, Version*) : + constexpr PrvIterator() noexcept : Iterator(), Type(PrvVer) {} + constexpr PrvIterator(pkgCache &Owner, Provides *Trg, Version*) noexcept : Iterator(Owner, Trg), Type(PrvVer) { if (S == 0) S = Owner.ProvideP; } - inline PrvIterator(pkgCache &Owner, Provides *Trg, Package*) : + constexpr PrvIterator(pkgCache &Owner, Provides *Trg, Package*) noexcept : Iterator(Owner, Trg), Type(PrvPkg) { if (S == 0) S = Owner.ProvideP; @@ -466,7 +466,7 @@ class APT_PUBLIC pkgCache::PrvIterator : public Iterator // Release file /*{{{*/ class APT_PUBLIC pkgCache::RlsFileIterator : public Iterator { public: - inline ReleaseFile* OwnerPointer() const { + constexpr ReleaseFile* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->RlsFileP : 0; } @@ -475,27 +475,27 @@ class APT_PUBLIC pkgCache::RlsFileIterator : public IteratorFileName == 0?0:Owner->StrP + S->FileName;} - inline const char *Archive() const {return S->Archive == 0?0:Owner->StrP + S->Archive;} - inline const char *Version() const {return S->Version == 0?0:Owner->StrP + S->Version;} - inline const char *Origin() const {return S->Origin == 0?0:Owner->StrP + S->Origin;} - inline const char *Codename() const {return S->Codename ==0?0:Owner->StrP + S->Codename;} - inline const char *Label() const {return S->Label == 0?0:Owner->StrP + S->Label;} - inline const char *Site() const {return S->Site == 0?0:Owner->StrP + S->Site;} - inline bool Flagged(pkgCache::Flag::ReleaseFileFlags const flag) const {return (S->Flags & flag) == flag; } + constexpr const char *FileName() const noexcept {return S->FileName == 0?0:Owner->StrP + S->FileName;} + constexpr const char *Archive() const noexcept {return S->Archive == 0?0:Owner->StrP + S->Archive;} + constexpr const char *Version() const noexcept {return S->Version == 0?0:Owner->StrP + S->Version;} + constexpr const char *Origin() const noexcept {return S->Origin == 0?0:Owner->StrP + S->Origin;} + constexpr const char *Codename() const noexcept {return S->Codename ==0?0:Owner->StrP + S->Codename;} + constexpr const char *Label() const noexcept {return S->Label == 0?0:Owner->StrP + S->Label;} + constexpr const char *Site() const noexcept {return S->Site == 0?0:Owner->StrP + S->Site;} + constexpr bool Flagged(pkgCache::Flag::ReleaseFileFlags const flag) const noexcept {return (S->Flags & flag) == flag; } std::string RelStr(); // Constructors - inline RlsFileIterator() : Iterator() {} - explicit inline RlsFileIterator(pkgCache &Owner) : Iterator(Owner, Owner.RlsFileP) {} - inline RlsFileIterator(pkgCache &Owner,ReleaseFile *Trg) : Iterator(Owner, Trg) {} + constexpr RlsFileIterator() noexcept : Iterator() {} + explicit constexpr RlsFileIterator(pkgCache &Owner) noexcept : Iterator(Owner, Owner.RlsFileP) {} + constexpr RlsFileIterator(pkgCache &Owner,ReleaseFile *Trg) noexcept : Iterator(Owner, Trg) {} }; /*}}}*/ // Package file /*{{{*/ class APT_PUBLIC pkgCache::PkgFileIterator : public Iterator { public: - inline PackageFile* OwnerPointer() const { + constexpr PackageFile* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->PkgFileP : 0; } @@ -504,32 +504,32 @@ class APT_PUBLIC pkgCache::PkgFileIterator : public IteratorFileName == 0?0:Owner->StrP + S->FileName;} - inline pkgCache::RlsFileIterator ReleaseFile() const {return RlsFileIterator(*Owner, Owner->RlsFileP + S->Release);} - inline const char *Archive() const {return S->Release == 0 ? Component() : ReleaseFile().Archive();} - inline const char *Version() const {return S->Release == 0 ? NULL : ReleaseFile().Version();} - inline const char *Origin() const {return S->Release == 0 ? NULL : ReleaseFile().Origin();} - inline const char *Codename() const {return S->Release == 0 ? NULL : ReleaseFile().Codename();} - inline const char *Label() const {return S->Release == 0 ? NULL : ReleaseFile().Label();} - inline const char *Site() const {return S->Release == 0 ? NULL : ReleaseFile().Site();} - inline bool Flagged(pkgCache::Flag::ReleaseFileFlags const flag) const {return S->Release== 0 ? false : ReleaseFile().Flagged(flag);} - inline bool Flagged(pkgCache::Flag::PkgFFlags const flag) const {return (S->Flags & flag) == flag;} - inline const char *Component() const {return S->Component == 0?0:Owner->StrP + S->Component;} - inline const char *Architecture() const {return S->Architecture == 0?0:Owner->StrP + S->Architecture;} - inline const char *IndexType() const {return S->IndexType == 0?0:Owner->StrP + S->IndexType;} + constexpr const char *FileName() const noexcept {return S->FileName == 0?0:Owner->StrP + S->FileName;} + constexpr pkgCache::RlsFileIterator ReleaseFile() const noexcept {return RlsFileIterator(*Owner, Owner->RlsFileP + S->Release);} + constexpr const char *Archive() const noexcept {return S->Release == 0 ? Component() : ReleaseFile().Archive();} + constexpr const char *Version() const noexcept {return S->Release == 0 ? NULL : ReleaseFile().Version();} + constexpr const char *Origin() const noexcept {return S->Release == 0 ? NULL : ReleaseFile().Origin();} + constexpr const char *Codename() const noexcept {return S->Release == 0 ? NULL : ReleaseFile().Codename();} + constexpr const char *Label() const noexcept {return S->Release == 0 ? NULL : ReleaseFile().Label();} + constexpr const char *Site() const noexcept {return S->Release == 0 ? NULL : ReleaseFile().Site();} + constexpr bool Flagged(pkgCache::Flag::ReleaseFileFlags const flag) const noexcept {return S->Release== 0 ? false : ReleaseFile().Flagged(flag);} + constexpr bool Flagged(pkgCache::Flag::PkgFFlags const flag) const noexcept {return (S->Flags & flag) == flag;} + constexpr const char *Component() const noexcept {return S->Component == 0?0:Owner->StrP + S->Component;} + constexpr const char *Architecture() const noexcept {return S->Architecture == 0?0:Owner->StrP + S->Architecture;} + constexpr const char *IndexType() const noexcept {return S->IndexType == 0?0:Owner->StrP + S->IndexType;} std::string RelStr(); // Constructors - inline PkgFileIterator() : Iterator() {} - explicit inline PkgFileIterator(pkgCache &Owner) : Iterator(Owner, Owner.PkgFileP) {} - inline PkgFileIterator(pkgCache &Owner,PackageFile *Trg) : Iterator(Owner, Trg) {} + constexpr PkgFileIterator() noexcept : Iterator() {} + explicit constexpr PkgFileIterator(pkgCache &Owner) noexcept : Iterator(Owner, Owner.PkgFileP) {} + constexpr PkgFileIterator(pkgCache &Owner,PackageFile *Trg) noexcept : Iterator(Owner, Trg) {} }; /*}}}*/ // Version File /*{{{*/ class APT_PUBLIC pkgCache::VerFileIterator : public pkgCache::Iterator { public: - inline VerFile* OwnerPointer() const { + constexpr VerFile* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->VerFileP : 0; } @@ -538,16 +538,16 @@ class APT_PUBLIC pkgCache::VerFileIterator : public pkgCache::IteratorPkgFileP + S->File);} + constexpr PkgFileIterator File() const noexcept {return PkgFileIterator(*Owner, Owner->PkgFileP + S->File);} - inline VerFileIterator() : Iterator() {} - inline VerFileIterator(pkgCache &Owner,VerFile *Trg) : Iterator(Owner, Trg) {} + constexpr VerFileIterator() noexcept : Iterator() {} + constexpr VerFileIterator(pkgCache &Owner,VerFile *Trg) noexcept : Iterator(Owner, Trg) {} }; /*}}}*/ // Description File /*{{{*/ class APT_PUBLIC pkgCache::DescFileIterator : public Iterator { public: - inline DescFile* OwnerPointer() const { + constexpr DescFile* OwnerPointer() const noexcept { return (Owner != 0) ? Owner->DescFileP : 0; } @@ -556,36 +556,36 @@ class APT_PUBLIC pkgCache::DescFileIterator : public IteratorPkgFileP + S->File);} + constexpr PkgFileIterator File() const noexcept {return PkgFileIterator(*Owner, Owner->PkgFileP + S->File);} - inline DescFileIterator() : Iterator() {} - inline DescFileIterator(pkgCache &Owner,DescFile *Trg) : Iterator(Owner, Trg) {} + constexpr DescFileIterator() noexcept : Iterator() {} + constexpr DescFileIterator(pkgCache &Owner,DescFile *Trg) noexcept : Iterator(Owner, Trg) {} }; /*}}}*/ // Inlined Begin functions can't be in the class because of order problems /*{{{*/ -inline pkgCache::PkgIterator pkgCache::GrpIterator::PackageList() const +constexpr pkgCache::PkgIterator pkgCache::GrpIterator::PackageList() const noexcept {return PkgIterator(*Owner,Owner->PkgP + S->FirstPackage);} - inline pkgCache::VerIterator pkgCache::GrpIterator::VersionsInSource() const + constexpr pkgCache::VerIterator pkgCache::GrpIterator::VersionsInSource() const noexcept { return VerIterator(*Owner, Owner->VerP + S->VersionsInSource); } -inline pkgCache::VerIterator pkgCache::PkgIterator::VersionList() const +constexpr pkgCache::VerIterator pkgCache::PkgIterator::VersionList() const noexcept {return VerIterator(*Owner,Owner->VerP + S->VersionList);} -inline pkgCache::VerIterator pkgCache::PkgIterator::CurrentVer() const +constexpr pkgCache::VerIterator pkgCache::PkgIterator::CurrentVer() const noexcept {return VerIterator(*Owner,Owner->VerP + S->CurrentVer);} -inline pkgCache::DepIterator pkgCache::PkgIterator::RevDependsList() const +constexpr pkgCache::DepIterator pkgCache::PkgIterator::RevDependsList() const noexcept {return DepIterator(*Owner,Owner->DepP + S->RevDepends,S);} -inline pkgCache::PrvIterator pkgCache::PkgIterator::ProvidesList() const +constexpr pkgCache::PrvIterator pkgCache::PkgIterator::ProvidesList() const noexcept {return PrvIterator(*Owner,Owner->ProvideP + S->ProvidesList,S);} -inline pkgCache::DescIterator pkgCache::VerIterator::DescriptionList() const +constexpr pkgCache::DescIterator pkgCache::VerIterator::DescriptionList() const noexcept {return DescIterator(*Owner,Owner->DescP + S->DescriptionList);} -inline pkgCache::PrvIterator pkgCache::VerIterator::ProvidesList() const +constexpr pkgCache::PrvIterator pkgCache::VerIterator::ProvidesList() const noexcept {return PrvIterator(*Owner,Owner->ProvideP + S->ProvidesList,S);} -inline pkgCache::DepIterator pkgCache::VerIterator::DependsList() const +constexpr pkgCache::DepIterator pkgCache::VerIterator::DependsList() const noexcept {return DepIterator(*Owner,Owner->DepP + S->DependsList,S);} -inline pkgCache::VerFileIterator pkgCache::VerIterator::FileList() const +constexpr pkgCache::VerFileIterator pkgCache::VerIterator::FileList() const noexcept {return VerFileIterator(*Owner,Owner->VerFileP + S->FileList);} -inline pkgCache::DescFileIterator pkgCache::DescIterator::FileList() const +constexpr pkgCache::DescFileIterator pkgCache::DescIterator::FileList() const noexcept {return DescFileIterator(*Owner,Owner->DescFileP + S->FileList);} /*}}}*/ #endif diff --git a/apt-pkg/pkgcache.h b/apt-pkg/pkgcache.h index 042d928e1..27719b75b 100644 --- a/apt-pkg/pkgcache.h +++ b/apt-pkg/pkgcache.h @@ -98,21 +98,21 @@ typedef uint16_t map_fileid_t; template class map_pointer { uint32_t val; public: - map_pointer() noexcept : val(0) {} - map_pointer(std::nullptr_t) noexcept : val(0) {} - explicit map_pointer(uint32_t n) noexcept : val(n) {} - explicit operator uint32_t() noexcept { return val; } - explicit operator bool() noexcept { return val != 0; } + constexpr map_pointer() noexcept : val(0) {} + constexpr map_pointer(std::nullptr_t) noexcept : val(0) {} + explicit constexpr map_pointer(uint32_t n) noexcept : val(n) {} + explicit constexpr operator uint32_t() const noexcept { return val; } + explicit constexpr operator bool() const noexcept { return val != 0; } }; -template inline T *operator +(T *p, map_pointer m) { return p + uint32_t(m); } -template inline bool operator ==(map_pointer u, map_pointer m) { return uint32_t(u) == uint32_t(m); } -template inline bool operator !=(map_pointer u, map_pointer m) { return uint32_t(u) != uint32_t(m); } -template inline bool operator <(map_pointer u, map_pointer m) { return uint32_t(u) < uint32_t(m); } -template inline bool operator >(map_pointer u, map_pointer m) { return uint32_t(u) > uint32_t(m); } -template inline uint32_t operator -(map_pointer u, map_pointer m) { return uint32_t(u) - uint32_t(m); } -template bool operator ==(map_pointer m, std::nullptr_t) { return uint32_t(m) == 0; } -template bool operator !=(map_pointer m, std::nullptr_t) { return uint32_t(m) != 0; } +template constexpr T *operator +(T *p, map_pointer m) noexcept { return p + uint32_t(m); } +template constexpr bool operator ==(map_pointer u, map_pointer m) noexcept{ return uint32_t(u) == uint32_t(m); } +template constexpr bool operator !=(map_pointer u, map_pointer m) noexcept{ return uint32_t(u) != uint32_t(m); } +template constexpr bool operator <(map_pointer u, map_pointer m) noexcept { return uint32_t(u) < uint32_t(m); } +template constexpr bool operator >(map_pointer u, map_pointer m) noexcept { return uint32_t(u) > uint32_t(m); } +template constexpr uint32_t operator -(map_pointer u, map_pointer m) noexcept { return uint32_t(u) - uint32_t(m); } +template constexpr bool operator ==(map_pointer m, std::nullptr_t) noexcept { return uint32_t(m) == 0; } +template constexpr bool operator !=(map_pointer m, std::nullptr_t) noexcept { return uint32_t(m) != 0; } // same as the previous, but documented to be to a string item typedef map_pointer map_stringitem_t; @@ -262,18 +262,18 @@ class APT_PUBLIC pkgCache /*{{{*/ return {name, len}; } - Header &Head() {return *HeaderP;} - inline GrpIterator GrpBegin(); - inline GrpIterator GrpEnd(); - inline PkgIterator PkgBegin(); - inline PkgIterator PkgEnd(); - inline PkgFileIterator FileBegin(); - inline PkgFileIterator FileEnd(); - inline RlsFileIterator RlsFileBegin(); - inline RlsFileIterator RlsFileEnd(); + constexpr Header &Head() noexcept {return *HeaderP;} + inline GrpIterator GrpBegin() noexcept; + constexpr GrpIterator GrpEnd() noexcept; + inline PkgIterator PkgBegin() noexcept; + constexpr PkgIterator PkgEnd() noexcept; + inline PkgFileIterator FileBegin() noexcept; + constexpr PkgFileIterator FileEnd() noexcept; + inline RlsFileIterator RlsFileBegin() noexcept; + constexpr RlsFileIterator RlsFileEnd() noexcept; - inline bool MultiArchCache() const { return MultiArchEnabled; } - inline char const * NativeArch(); + constexpr bool MultiArchCache() const noexcept { return MultiArchEnabled; } + constexpr char const * NativeArch() const noexcept; // Make me a function pkgVersioningSystem *VS; @@ -822,29 +822,29 @@ struct pkgCache::Provides }; /*}}}*/ -inline char const * pkgCache::NativeArch() +constexpr char const * pkgCache::NativeArch() const noexcept { return StrP + HeaderP->Architecture; } #include - inline pkgCache::GrpIterator pkgCache::GrpBegin() + inline pkgCache::GrpIterator pkgCache::GrpBegin() noexcept { return GrpIterator(*this); } - inline pkgCache::GrpIterator pkgCache::GrpEnd() + constexpr pkgCache::GrpIterator pkgCache::GrpEnd() noexcept { return GrpIterator(*this, GrpP);} -inline pkgCache::PkgIterator pkgCache::PkgBegin() +inline pkgCache::PkgIterator pkgCache::PkgBegin() noexcept {return PkgIterator(*this);} -inline pkgCache::PkgIterator pkgCache::PkgEnd() +constexpr pkgCache::PkgIterator pkgCache::PkgEnd() noexcept {return PkgIterator(*this,PkgP);} -inline pkgCache::PkgFileIterator pkgCache::FileBegin() +inline pkgCache::PkgFileIterator pkgCache::FileBegin() noexcept {return PkgFileIterator(*this,PkgFileP + HeaderP->FileList);} -inline pkgCache::PkgFileIterator pkgCache::FileEnd() +constexpr pkgCache::PkgFileIterator pkgCache::FileEnd() noexcept {return PkgFileIterator(*this,PkgFileP);} -inline pkgCache::RlsFileIterator pkgCache::RlsFileBegin() +inline pkgCache::RlsFileIterator pkgCache::RlsFileBegin() noexcept {return RlsFileIterator(*this,RlsFileP + HeaderP->RlsFileList);} -inline pkgCache::RlsFileIterator pkgCache::RlsFileEnd() +constexpr pkgCache::RlsFileIterator pkgCache::RlsFileEnd() noexcept {return RlsFileIterator(*this,RlsFileP);} -- cgit v1.2.3-70-g09d2 From 42dab6a46748ac80f0217212932b74af1af94017 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 6 Feb 2026 15:50:04 +0100 Subject: solver3: Use constexpr and noexcept in most places Aside from Clause, which initializes an std::vector and an std::forward_list, which do not have constexpr constructors in C++17, we can turn our inline functions constexpr. Using `constexpr` implies `inline`, so simplify that accordingly where needed. Adding noexcept to the function allows STL components to utilize more optimized code paths. Marking SameOrGroup as constexpr significantly improves performance due to being in the hot path and it now being inlined - removing branching by 10%. iolveiolver --- apt-pkg/solver3.cc | 24 ++++++++++-------- apt-pkg/solver3.h | 74 +++++++++++++++++++++++++++--------------------------- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index d8d350dc5..6d818ed67 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -18,8 +18,6 @@ * classic APT solver. */ -#define APT_COMPILING_APT - #include #include @@ -59,7 +57,7 @@ Solver::Solver(pkgCache &cache) Solver::~Solver() = default; // This function determines if a work item is less important than another. -bool Solver::Work::operator<(Solver::Work const &b) const +constexpr bool Solver::Work::operator<(Solver::Work const &b) const noexcept { if ((not clause->optional && size < 2) != (not b.clause->optional && b.size < 2)) return not b.clause->optional && b.size < 2; @@ -130,7 +128,7 @@ std::string Solver::Work::toString(pkgCache &cache) const return out.str(); } -inline Var Solver::bestReason(Clause const *clause, Var var) const +constexpr Var Solver::bestReason(Clause const *clause, Var var) const noexcept { if (not clause) return Var{}; @@ -145,7 +143,7 @@ inline Var Solver::bestReason(Clause const *clause, Var var) const return clause->reason; } -inline LiftedBool Solver::value(Lit lit) const +constexpr LiftedBool Solver::value(Lit lit) const noexcept { return lit.sign() ? ~(*this)[lit.var()].assignment : (*this)[lit.var()].assignment; } @@ -417,9 +415,9 @@ bool Solver::Propagate(const Clause *clause, Lit p) } // Check if the clause is unit, conflict, or undecided - auto not_false = [&](auto lit) { return value(lit) != LiftedBool::False; }; + auto not_false = [&](auto lit) noexcept { return value(lit) != LiftedBool::False; }; auto head = std::ranges::find_if(clause->solutions, not_false); - auto find_tail = [&]() { return std::ranges::find_last_if(clause->solutions, not_false); }; + auto find_tail = [&]() noexcept { return std::ranges::find_last_if(clause->solutions, not_false); }; if (head == clause->solutions.end()) { // Conflict clause. If this clause is non-optional; reject its "reason". @@ -773,7 +771,11 @@ DependencySolver::DependencySolver(pkgCache &cache, pkgDepCache::Policy &policy, DependencySolver::~DependencySolver() = default; -static bool SameOrGroup(pkgCache::DepIterator a, pkgCache::DepIterator b) +// This is called in the hot path of Discover() as we are trying to determine if a dependency +// is the same in all packages. +// Marking it constexpr causes it to be inlined, which significantly improves solver performance, +// around 5%; but it produces more LL cache misses. +constexpr bool SameOrGroup(pkgCache::DepIterator a, pkgCache::DepIterator b) noexcept { while (1) { @@ -813,14 +815,14 @@ const Clause *DependencySolver::RegisterClause(Clause &&clause) earlierDep.end() || (earlierDep->CompareOp & pkgCache::Dep::Or) || earlierDep.TargetPkg() != dep.TargetPkg()) continue; - if (std::none_of(earlierClause->solutions.begin(), earlierClause->solutions.end(), [&clause](auto earlierSol) + if (std::none_of(earlierClause->solutions.begin(), earlierClause->solutions.end(), [&clause](auto earlierSol) noexcept { return std::ranges::contains(clause.solutions, earlierSol); })) continue; if (earlierClause->optional == clause.optional) { - std::erase_if(earlierClause->solutions, [&clause, this](auto earlierSol) + std::erase_if(earlierClause->solutions, [&clause, this](auto earlierSol) noexcept { return not std::ranges::contains(clause.solutions, earlierSol); }); @@ -830,7 +832,7 @@ const Clause *DependencySolver::RegisterClause(Clause &&clause) else if (clause.optional) { // If say a Depends has fewer solution than a Recommends, remove the Recommend's extranous ones. - std::erase_if(clause.solutions, [&earlierClause, this](auto sol) + std::erase_if(clause.solutions, [&earlierClause, this](auto sol) noexcept { return not std::ranges::contains(earlierClause->solutions, sol); }); diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h index 4390d589b..380aa3ad6 100644 --- a/apt-pkg/solver3.h +++ b/apt-pkg/solver3.h @@ -56,8 +56,8 @@ class ContiguousCacheMap data_ = new V[size]{}; } - V &operator[](const K *key) { return data_[key->ID]; } - const V &operator[](const K *key) const { return data_[key->ID]; } + constexpr V &operator[](const K *key) noexcept { return data_[key->ID]; } + constexpr const V &operator[](const K *key) const noexcept { return data_[key->ID]; } ~ContiguousCacheMap() { delete[] data_; } // Delete copy constructors for memory safety (rule of 3) @@ -136,45 +136,45 @@ struct Var { uint32_t value; - explicit constexpr Var(uint32_t value = 0) : value{value} {} - explicit Var(pkgCache::PkgIterator const &Pkg) : value(uint32_t(Pkg.MapPointer()) << 1) {} - explicit Var(pkgCache::VerIterator const &Ver) : value(uint32_t(Ver.MapPointer()) << 1 | 1) {} + explicit constexpr Var(uint32_t value = 0) noexcept : value{value} {} + explicit Var(pkgCache::PkgIterator const &Pkg) noexcept : value(uint32_t(Pkg.MapPointer()) << 1) {} + explicit Var(pkgCache::VerIterator const &Ver) noexcept : value(uint32_t(Ver.MapPointer()) << 1 | 1) {} - inline constexpr bool isVersion() const { return value & 1; } - inline constexpr uint32_t mapPtr() const { return value >> 1; } + constexpr bool isVersion() const noexcept { return value & 1; } + constexpr uint32_t mapPtr() const noexcept { return value >> 1; } // \brief Return the package, if any, otherwise 0. - map_pointer Pkg() const + constexpr map_pointer Pkg() const noexcept { return isVersion() ? 0 : map_pointer{mapPtr()}; } // \brief Return the version, if any, otherwise 0. - map_pointer Ver() const + constexpr map_pointer Ver() const noexcept { return isVersion() ? map_pointer{mapPtr()} : 0; } // \brief Return the package iterator if storing a package, or an empty one - pkgCache::PkgIterator Pkg(pkgCache &cache) const + constexpr pkgCache::PkgIterator Pkg(pkgCache &cache) const noexcept { return isVersion() ? pkgCache::PkgIterator() : pkgCache::PkgIterator(cache, cache.PkgP + Pkg()); } // \brief Return the version iterator if storing a package, or an empty end. - pkgCache::VerIterator Ver(pkgCache &cache) const + constexpr pkgCache::VerIterator Ver(pkgCache &cache) const noexcept { return isVersion() ? pkgCache::VerIterator(cache, cache.VerP + Ver()) : pkgCache::VerIterator(); } // \brief Return a package, cast from version if needed - pkgCache::PkgIterator CastPkg(pkgCache &cache) const + constexpr pkgCache::PkgIterator CastPkg(pkgCache &cache) const noexcept { return isVersion() ? Ver(cache).ParentPkg() : Pkg(cache); } // \brief Check if there is no reason. - constexpr bool empty() const { return value == 0; } + constexpr bool empty() const noexcept { return value == 0; } constexpr bool operator!=(Var const other) const noexcept { return value != other.value; } constexpr bool operator==(Var const other) const noexcept { return value == other.value; } /// \brief Negate - constexpr Lit operator~() const; + constexpr Lit operator~() const noexcept; std::string toString(pkgCache &cache) const { @@ -196,21 +196,21 @@ struct Lit private: friend struct std::hash; // Private constructor from a number, to be used with operator~ - explicit constexpr Lit(int32_t value) : value{value} {} + explicit constexpr Lit(int32_t value) noexcept : value{value} {} int32_t value; public: - constexpr Lit() : value{0} {} + constexpr Lit() noexcept : value{0} {} // SAFETY: value must be 31 bit, one bit is needed for the sign. - constexpr Lit(Var var) : value{static_cast(var.value)} {} + constexpr Lit(Var var) noexcept : value{static_cast(var.value)} {} // Accessors - constexpr Var var() const { return Var(std::abs(value)); } - constexpr bool sign() const { return value < 0; } - constexpr Lit operator~() const { return Lit(-value); } + constexpr Var var() const noexcept { return Var(std::abs(value)); } + constexpr bool sign() const noexcept { return value < 0; } + constexpr Lit operator~() const noexcept { return Lit(-value); } // Properties - constexpr bool empty() const { return value == 0; } + constexpr bool empty() const noexcept { return value == 0; } constexpr bool operator!=(Lit const other) const noexcept { return value != other.value; } constexpr bool operator==(Lit const other) const noexcept { return value == other.value; } @@ -250,12 +250,12 @@ struct Clause std::string toString(pkgCache &cache, bool pretty = false, bool showMerged = true) const; }; -constexpr Lit Solver::Var::operator~() const +constexpr Lit Solver::Var::operator~() const noexcept { return ~Lit(*this); } -inline LiftedBool operator~(LiftedBool value) +constexpr LiftedBool operator~(LiftedBool value) noexcept { switch (value) { @@ -307,29 +307,29 @@ class Solver ContiguousCacheMap verStates; // \brief Helper function for safe access to package state. - inline State &operator[](const pkgCache::Package *P) + constexpr State &operator[](const pkgCache::Package *P) noexcept { return pkgStates[P]; } - inline const State &operator[](const pkgCache::Package *P) const + constexpr const State &operator[](const pkgCache::Package *P) const noexcept { return pkgStates[P]; } // \brief Helper function for safe access to version state. - inline State &operator[](const pkgCache::Version *V) + constexpr State &operator[](const pkgCache::Version *V) noexcept { return verStates[V]; } - inline const State &operator[](const pkgCache::Version *V) const + constexpr const State &operator[](const pkgCache::Version *V) const noexcept { return verStates[V]; } // \brief Helper function for safe access to either state. - inline State &operator[](Var r); - inline const State &operator[](Var r) const; + constexpr State &operator[](Var r) noexcept; + constexpr const State &operator[](Var r) const noexcept; - inline std::vector &watches(Lit lit); + constexpr std::vector &watches(Lit lit) noexcept; // \brief Heap of the remaining work. // @@ -375,8 +375,8 @@ class Solver { return static_cast(trailLim.size()); } - inline Var bestReason(Clause const *clause, Var var) const; - inline LiftedBool value(Lit lit) const; + constexpr Var bestReason(Clause const *clause, Var var) const noexcept; + constexpr LiftedBool value(Lit lit) const noexcept; public: // \brief Revert to the previous decision level. @@ -514,9 +514,9 @@ struct APT::Solver::Solver::Work /// Number of valid choices at insertion time size_t size{0}; - bool operator<(APT::Solver::Solver::Work const &b) const; + constexpr bool operator<(APT::Solver::Solver::Work const &b) const noexcept; std::string toString(pkgCache &cache) const; - inline Work(const Clause *clause, level_type level) : clause(clause), level(level) {} + constexpr Work(const Clause *clause, level_type level) noexcept : clause(clause), level(level) {} }; /** @@ -582,7 +582,7 @@ struct APT::Solver::Solver::Trail std::optional work; }; -inline APT::Solver::Solver::State &APT::Solver::Solver::operator[](APT::Solver::Var r) +constexpr APT::Solver::Solver::State &APT::Solver::Solver::operator[](APT::Solver::Var r) noexcept { if (auto P = r.Pkg()) return (*this)[cache.PkgP + P]; @@ -591,7 +591,7 @@ inline APT::Solver::Solver::State &APT::Solver::Solver::operator[](APT::Solver:: return *rootState.get(); } -inline const APT::Solver::Solver::State &APT::Solver::Solver::operator[](APT::Solver::Var r) const +constexpr const APT::Solver::Solver::State &APT::Solver::Solver::operator[](APT::Solver::Var r) const noexcept { return const_cast(*this)[r]; } @@ -612,7 +612,7 @@ struct std::hash std::size_t operator()(const APT::Solver::Lit &v) const noexcept { return hash_value(v.value); } }; -inline std::vector &APT::Solver::Solver::watches(Lit lit) +constexpr std::vector &APT::Solver::Solver::watches(Lit lit) noexcept { return (*this)[lit.var()].watches[lit.sign()]; } -- cgit v1.2.3-70-g09d2