[llvm] [ADT] Introduce EytzingerTableSpan (PR #208885)
Kazu Hirata via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 12 20:16:12 PDT 2026
================
@@ -0,0 +1,115 @@
+//===- Eytzinger.h - Eytzinger Search Tree Span -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines the EytzingerTableSpan class, a non-owning view of a
+/// buffer formatted as a complete binary search tree in Eytzinger
+/// (breadth-first) order.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_EYTZINGER_H
+#define LLVM_ADT_EYTZINGER_H
+
+#include <cassert>
+#include <cstddef>
+#include <optional>
+
+namespace llvm {
+
+/// Non-owning view of a buffer formatted as a complete binary search tree in
+/// Eytzinger (breadth-first) order.
+template <typename T> class EytzingerTableSpan {
+public:
+ EytzingerTableSpan() = default;
+ EytzingerTableSpan(const T *Data, size_t NumEntries)
+ : Data(Data), NumEntries(NumEntries) {}
+
+ [[nodiscard]] const T *data() const { return Data; }
+ [[nodiscard]] bool empty() const { return !Data || NumEntries == 0; }
+ [[nodiscard]] size_t size() const { return NumEntries; }
+ [[nodiscard]] const T &operator[](size_t Idx) const {
+ assert(Idx < NumEntries && "Index out of bounds");
+ return Data[Idx];
+ }
+
+ /// Search this Eytzinger table for Target. Returns the 0-based array index if
+ /// found.
+ ///
+ /// KeyT enables heterogeneous lookups, allowing callers to search tables of
+ /// endian-specific wrappers (e.g., support::ulittle64_t) using native integer
+ /// keys without explicit conversions at the call site.
+ template <typename KeyT = T>
+ [[nodiscard]] std::optional<size_t> findIndex(const KeyT &Target) const {
+ size_t I = 0;
+ while (I < NumEntries) {
+ if (Data[I] == Target)
+ return I;
+ I = 2 * I + 1 + (Data[I] < Target);
+ }
+ return std::nullopt;
+ }
+
+ /// Check if this Eytzinger table contains Target.
+ template <typename KeyT = T>
+ [[nodiscard]] bool contains(const KeyT &Target) const {
+ return findIndex(Target).has_value();
+ }
+
+ /// Verify whether the buffer satisfies strictly ascending binary search tree
+ /// order in Eytzinger layout. Runs iteratively in O(N) time and O(1) space.
+ [[nodiscard]] bool isSorted() const {
+ if (empty())
+ return true;
+
+ auto Left = [](size_t I) { return 2 * I + 1; };
+ auto Right = [](size_t I) { return 2 * I + 2; };
+ auto Parent = [](size_t I) { return (I - 1) / 2; };
+ auto IsRightChild = [](size_t I) { return I > 0 && I % 2 == 0; };
+ auto HasLeft = [&](size_t I) { return Left(I) < NumEntries; };
+ auto HasRight = [&](size_t I) { return Right(I) < NumEntries; };
+
+ // Start at the leftmost leaf (in-order minimum).
----------------
kazutakahirata wrote:
It's not sufficient to check immediate parent-child relationships.
For example, consider:
```
40
/ \
20 60
/ \ / \
10 50 55 70
^
* 20 < 50 passes local check, but 50 < 40 does not hold!
```
Every node satisfies immediate parent-child relationships, but we do not satisfy 50 < 40 even though 50 is in the left subtree.
To guarantee correctness for `findIndex`, `isSorted()` must verify that the full in-order traversal is strictly ascending (`Prev < Curr`). The current implementation achieves this iterative in-order walk in `O(N)` time and `O(1)` space without a recursive call stack.
https://github.com/llvm/llvm-project/pull/208885
More information about the llvm-commits
mailing list