about summary refs log tree commit diff
path: root/third_party/nix/src/libexpr/attr-set.cc
diff options
context:
space:
mode:
authorGriffin Smith <grfn@gws.fyi>2020-07-12T20·51-0400
committerglittershark <grfn@gws.fyi>2020-07-13T23·50+0000
commitd5505fcff9dc9ad76b4cb822cc642fdd0e238553 (patch)
treef7d9564262fd99ffa7ac37f78ff18254055a9581 /third_party/nix/src/libexpr/attr-set.cc
parent98148e671130c93d5c292ab560628eb8b8acee8a (diff)
feat(3p/nix): Add vector-backed impl for Bindings r/1284
Add an alternative impl of the now-abstract Bindings base class that is
backed by a std::vector, somewhat similar but stylistically a little
superior to the array-backed implementation in upstream nix. The
underlying iterator type in BindingsIterator is now backed by a
std::variant that we std::visit an overload over in order to implement
the various bits of the iterator interface.

Paired-With: Luke Granger-Brown <git@lukegb.com>
Paired-With: Vincent Ambo <mail@tazj.in>
Paired-With: Perry Lorier <isomer@tvl.fyi>
Change-Id: I7fbd1f4d5c449e2f9b82102a701b0bacd5e80672
Reviewed-on: https://cl.tvl.fyi/c/depot/+/1123
Tested-by: BuildkiteCI
Reviewed-by: tazjin <mail@tazj.in>
Diffstat (limited to 'third_party/nix/src/libexpr/attr-set.cc')
-rw-r--r--third_party/nix/src/libexpr/attr-set.cc146
1 files changed, 138 insertions, 8 deletions
diff --git a/third_party/nix/src/libexpr/attr-set.cc b/third_party/nix/src/libexpr/attr-set.cc
index 3d1005fd0f7f..053d7d6f6c5e 100644
--- a/third_party/nix/src/libexpr/attr-set.cc
+++ b/third_party/nix/src/libexpr/attr-set.cc
@@ -7,25 +7,56 @@
 #include <glog/logging.h>
 
 #include "libexpr/eval-inline.hh"
+#include "libutil/visitor.hh"
 
 namespace nix {
 
 BindingsIterator& BindingsIterator::operator++() {
-  _iterator++;
+  std::visit(util::overloaded{
+                 [](AttributeMap::iterator& iter) { ++iter; },
+                 [](AttributeVector::iterator& iter) { ++iter; },
+             },
+             _iterator);
   return *this;
 }
+
 BindingsIterator BindingsIterator::operator++(int) {
-  ++_iterator;
+  std::visit(util::overloaded{
+                 [](AttributeMap::iterator& iter) { iter++; },
+                 [](AttributeVector::iterator& iter) { iter++; },
+             },
+             _iterator);
   return *this;
 }
+
 bool BindingsIterator::operator==(const BindingsIterator& other) const {
   return _iterator == other._iterator;
 }
+
 bool BindingsIterator::operator!=(const BindingsIterator& other) const {
   return _iterator != other._iterator;
 }
+
 BindingsIterator::reference BindingsIterator::operator*() const {
-  return *_iterator;
+  return std::visit(
+      util::overloaded{
+          [](AttributeMap::iterator iter) -> std::pair<const Symbol, Attr>& {
+            return *iter;
+          },
+          [](AttributeVector::iterator iter) -> std::pair<const Symbol, Attr>& {
+            // this cast is effectively upcasting the left-hand side of the
+            // pair, which in the vector case *must* be const so that insert and
+            // friends can shift it around, but in the map case *must not* be
+            // const so that the key ordering semantics don't change out from
+            // under the map. So we pick const as the LUB of the two types and
+            // then upcast here. The static_assert, per the docs for
+            // reinterpret_cast, is proving that this is safe
+            static_assert(
+                std::is_standard_layout<std::pair<const Symbol, Attr>>::value);
+            return *reinterpret_cast<std::pair<const Symbol, Attr>*>(&*iter);
+          },
+      },
+      _iterator);
 }
 
 class BTreeBindings : public Bindings {
@@ -33,7 +64,7 @@ class BTreeBindings : public Bindings {
   size_t size() override;
   bool empty() override;
   void push_back(const Attr& attr) override;
-  void insert_or_assign(const Attr& attr) override;
+  void insert_or_assign(Attr& attr) override;
   Bindings::iterator find(const Symbol& name) override;
   Bindings::iterator begin() override;
   Bindings::iterator end() override;
@@ -44,8 +75,6 @@ class BTreeBindings : public Bindings {
   AttributeMap attributes_;
 };
 
-Bindings* Bindings::NewGC() { return new (GC) BTreeBindings; }
-
 // This function inherits its name from previous implementations, in
 // which Bindings was backed by an array of elements which was scanned
 // linearly.
@@ -66,7 +95,7 @@ void BTreeBindings::push_back(const Attr& attr) {
 }
 
 // Insert or assign (i.e. replace) a value in the attribute set.
-void BTreeBindings::insert_or_assign(const Attr& attr) {
+void BTreeBindings::insert_or_assign(Attr& attr) {
   attributes_.insert_or_assign(attr.name, attr);
 }
 
@@ -106,7 +135,9 @@ void BTreeBindings::merge(Bindings& other) {
 void EvalState::mkAttrs(Value& v, size_t capacity) {
   clearValue(v);
   v.type = tAttrs;
-  v.attrs = BTreeBindings::NewGC();
+  v.attrs = Bindings::NewGC();
+  assert(v.attrs->begin() == v.attrs->begin());
+  assert(v.attrs->end() == v.attrs->end());
   nrAttrsets++;
   nrAttrsInAttrsets += capacity;
 }
@@ -120,4 +151,103 @@ Value* EvalState::allocAttr(Value& vAttrs, const Symbol& name) {
   return v;
 }
 
+class VectorBindings : public Bindings {
+ public:
+  size_t size() override;
+  bool empty() override;
+  void push_back(const Attr& attr) override;
+  void insert_or_assign(Attr& attr) override;
+  Bindings::iterator find(const Symbol& name) override;
+  Bindings::iterator begin() override;
+  Bindings::iterator end() override;
+  void merge(Bindings& other) override;
+  [[deprecated]] virtual std::vector<const Attr*> lexicographicOrder() override;
+
+ private:
+  AttributeVector attributes_;
+};
+
+size_t VectorBindings::size() { return attributes_.size(); }
+
+bool VectorBindings::empty() { return attributes_.empty(); }
+
+// Insert or assign (i.e. replace) a value in the attribute set.
+void VectorBindings::insert_or_assign(Attr& attr) {
+  for (auto it = attributes_.begin(); it != attributes_.end(); ++it) {
+    if (it->first == attr.name) {
+      it->second = attr;
+      return;
+    } else if (attr.name < it->first) {
+      // TODO convert to BTreeMap if we get big enough
+      attributes_.emplace(it, attr.name, attr);
+      return;
+    }
+  }
+
+  attributes_.emplace_back(attr.name, attr);
+}
+
+void VectorBindings::merge(Bindings& other) {
+  AttributeVector new_attributes;
+  new_attributes.reserve(size() + other.size());
+
+  auto m_it = attributes_.begin();
+  auto other_it = other.begin();
+
+  while (other_it != other.end() && m_it != attributes_.end()) {
+    if (other_it->first < m_it->first) {
+      new_attributes.push_back(*(m_it++));
+    } else {
+      if (m_it->first == other_it->first) {
+        ++m_it;
+      }
+      new_attributes.push_back(*(other_it++));
+    }
+  }
+
+  if (m_it != attributes_.end()) {
+    std::copy(m_it, attributes_.end(), std::back_inserter(new_attributes));
+  }
+
+  if (other_it != other.end()) {
+    std::copy(other_it, other.end(), std::back_inserter(new_attributes));
+  }
+
+  new_attributes.shrink_to_fit();
+
+  attributes_ = new_attributes;
+}
+
+void VectorBindings::push_back(const Attr& attr) {
+  attributes_.emplace_back(attr.name, attr);
+}
+
+std::vector<const Attr*> VectorBindings::lexicographicOrder() {
+  std::vector<const Attr*> result(attributes_.size());
+
+  for (auto& [_, attr] : attributes_) {
+    result.push_back(&attr);
+  }
+
+  return result;
+}
+
+Bindings::iterator VectorBindings::find(const Symbol& name) {
+  return BindingsIterator{
+      std::find_if(attributes_.begin(), attributes_.end(),
+                   [&name](const auto& pair) { return pair.first == name; })};
+}
+
+Bindings::iterator VectorBindings::begin() {
+  return BindingsIterator{attributes_.begin()};
+}
+
+Bindings::iterator VectorBindings::end() {
+  return BindingsIterator{attributes_.end()};
+}
+
+// TODO pick what to do based on size
+Bindings* Bindings::NewGC() { return new (GC) BTreeBindings; }
+// Bindings* Bindings::NewGC() { return new (GC) VectorBindings; }
+
 }  // namespace nix