[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