packages feed

glean-0.2.0.0: glean/rts/stacked.h

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

#pragma once

#include "glean/rts/binary.h"
#include "glean/rts/factset.h"
#include "glean/rts/inventory.h"
#include "glean/rts/substitution.h"

namespace facebook {
namespace glean {
namespace rts {

template <typename Iface>
struct StackedBase : Iface {
  StackedBase(Lookup* b, Iface* s)
      : base(b), stacked(s), mid(stacked->startingId()) {}

  // Lookup implementation

  Id idByKey(Pid type, folly::ByteRange key) override {
    if (auto id = stacked->idByKey(type, key)) {
      return id;
    } else {
      auto bid = base->idByKey(type, key);
      return bid < mid ? bid : Id::invalid();
    }
  }

  Pid typeById(Id id) override {
    return id < mid ? base->typeById(id) : stacked->typeById(id);
  }

  bool factById(Id id, std::function<void(Pid, Fact::Clause)> f) override {
    return id < mid ? base->factById(id, std::move(f))
                    : stacked->factById(id, std::move(f));
  }

  Id startingId() const override {
    return base->startingId();
  }
  Id firstFreeId() const override {
    return stacked->firstFreeId();
  }

  Interval count(Pid pid) const override {
    return base->count(pid) + stacked->count(pid);
  }

  std::unique_ptr<FactIterator> enumerate(Id from, Id upto) override {
    if (from < mid) {
      if (upto && upto <= mid) {
        return base->enumerate(from, upto);
      } else {
        return FactIterator::append(
            base->enumerate(from, mid), stacked->enumerate(mid, upto));
      }
    } else {
      return stacked->enumerate(from, upto);
    }
  }

  std::unique_ptr<FactIterator> enumerateBack(Id from, Id downto) override {
    if (from && from <= mid) {
      return base->enumerateBack(from, downto);
    } else if (downto >= mid) {
      return stacked->enumerateBack(from, downto);
    } else {
      return FactIterator::append(
          stacked->enumerateBack(from, mid), base->enumerateBack(mid, downto));
    }
  }

  std::unique_ptr<FactIterator> seek(
      Pid type,
      folly::ByteRange prefix,
      std::optional<Fact::Ref> restart) override {
    if (restart.has_value()) {
      auto id = restart.value().id;
      if (id < mid) {
        return FactIterator::append(
            base->seekWithinSection(type, prefix, Id::invalid(), mid, restart),
            stacked->seek(type, prefix));
      } else {
        return stacked->seek(type, prefix, restart);
      }
    } else {
      return FactIterator::append(
          base->seekWithinSection(type, prefix, Id::invalid(), mid),
          stacked->seek(type, prefix));
    }
  }

  std::unique_ptr<FactIterator> seekWithinSection(
      Pid type,
      folly::ByteRange prefix,
      Id from,
      Id to,
      std::optional<Fact::Ref> restart) override {
    if (to <= mid) {
      return base->seekWithinSection(type, prefix, from, to, restart);
    } else if (mid <= from) {
      return stacked->seekWithinSection(type, prefix, from, to, restart);
    } else {
      if (restart.has_value()) {
        auto id = restart.value().id;
        if (id < mid) {
          return FactIterator::append(
              base->seekWithinSection(type, prefix, from, mid, restart),
              stacked->seekWithinSection(type, prefix, mid, to));
        } else {
          return stacked->seekWithinSection(type, prefix, mid, to, restart);
        }
      } else {
        return FactIterator::append(
            base->seekWithinSection(type, prefix, from, mid),
            stacked->seekWithinSection(type, prefix, mid, to));
      }
    }
  }

  UsetId getOwner(Id id) override {
    // The stacked DB may specify the owner for a fact in the base DB
    // due to propagation of ownership from facts in the stacked DB,
    // so we have to check the stacked DB first.
    auto set = stacked->getOwner(id);
    if (set != INVALID_USET) {
      return set;
    } else {
      return id < mid ? base->getOwner(id) : INVALID_USET;
    }
  }

 protected:
  Lookup* base;
  Iface* stacked;
  Id mid;
};

/**
 * A Stacked stacks a Lookup or Define on top of a base Lookup, allowing facts
 * to be looked up in both and new facts added to the stacked object if
 * supported. The stacked object's starting id determines the boundary between
 * the two - any facts in the base Lookup with an id >= that will be ignored.
 *
 *
 * This is an attempt to model the corresponding Haskell hierarchy in C++.
 */
template <typename Iface>
struct Stacked;

template <>
struct Stacked<Lookup> final : StackedBase<Lookup> {
  using StackedBase<Lookup>::StackedBase;
};

template <>
struct Stacked<Define> final : StackedBase<Define> {
  using StackedBase<Define>::StackedBase;

  // Define implementation

  Id define(Pid type, Fact::Clause clause, Id max_ref = Id::invalid())
      override {
    // TODO: We probably want to look up in stacked first but the current
    // interface doesn't support doing that without two lookups.

    // If the clause references a local fact then it can't possibly exist in the
    // global Lookup so don't bother looking it up there.
    if (max_ref < mid) {
      auto id = base->idByKey(type, clause.key());
      if (id && id < mid) {
        // NOTE: We assume that 'value' has been typechecked. If it is empty
        // then
        //       it must be the only inhabitant of its type (which must be () or
        //       similar). This means that there is no need to check against the
        //       value stored in the database as that must be empty, too.
        if (clause.value_size != 0) {
          [[maybe_unused]] bool found =
              base->factById(id, [&](auto, auto found) {
                if (clause.value() != found.value()) {
                  id = Id::invalid();
                }
              });
          assert(found);
        }
        return id;
      }
    }
    return stacked->define(type, clause, max_ref);
  }
};

} // namespace rts
} // namespace glean
} // namespace facebook