forked from lix-project/lix
8775be3393
this slightly increases the amount of memory used for any given symbol, but this increase is more than made up for if the symbol is referenced more than once in the EvalState that holds it. on average every symbol should be referenced at least twice (once to introduce a binding, once to use it), so we expect no increase in memory on average. symbol tables are limited to 2³² entries like position tables, and similar arguments apply to why overflow is not likely: 2³² symbols would require as many string instances (at 24 bytes each) and map entries (at 24 bytes or more each, assuming that the map holds on average at most one item per bucket as the docs say). a full symbol table would require at least 192GB of memory just for symbols, which is well out of reach. (an ofborg eval of nixpks today creates less than a million symbols!)
154 lines
3.4 KiB
C++
154 lines
3.4 KiB
C++
#pragma once
|
|
|
|
#include "nixexpr.hh"
|
|
#include "symbol-table.hh"
|
|
|
|
#include <algorithm>
|
|
#include <optional>
|
|
|
|
namespace nix {
|
|
|
|
|
|
class EvalState;
|
|
struct Value;
|
|
|
|
/* Map one attribute name to its value. */
|
|
struct Attr
|
|
{
|
|
SymbolIdx name;
|
|
Value * value;
|
|
PosIdx pos;
|
|
Attr(SymbolIdx name, Value * value, PosIdx pos = noPos)
|
|
: name(name), value(value), pos(pos) { };
|
|
Attr() { };
|
|
bool operator < (const Attr & a) const
|
|
{
|
|
return name < a.name;
|
|
}
|
|
};
|
|
|
|
/* Bindings contains all the attributes of an attribute set. It is defined
|
|
by its size and its capacity, the capacity being the number of Attr
|
|
elements allocated after this structure, while the size corresponds to
|
|
the number of elements already inserted in this structure. */
|
|
class Bindings
|
|
{
|
|
public:
|
|
typedef uint32_t size_t;
|
|
PosIdx pos;
|
|
|
|
private:
|
|
size_t size_, capacity_;
|
|
Attr attrs[0];
|
|
|
|
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
|
Bindings(const Bindings & bindings) = delete;
|
|
|
|
public:
|
|
size_t size() const { return size_; }
|
|
|
|
bool empty() const { return !size_; }
|
|
|
|
typedef Attr * iterator;
|
|
|
|
void push_back(const Attr & attr)
|
|
{
|
|
assert(size_ < capacity_);
|
|
attrs[size_++] = attr;
|
|
}
|
|
|
|
iterator find(const SymbolIdx & name)
|
|
{
|
|
Attr key(name, 0);
|
|
iterator i = std::lower_bound(begin(), end(), key);
|
|
if (i != end() && i->name == name) return i;
|
|
return end();
|
|
}
|
|
|
|
Attr * get(const SymbolIdx & name)
|
|
{
|
|
Attr key(name, 0);
|
|
iterator i = std::lower_bound(begin(), end(), key);
|
|
if (i != end() && i->name == name) return &*i;
|
|
return nullptr;
|
|
}
|
|
|
|
iterator begin() { return &attrs[0]; }
|
|
iterator end() { return &attrs[size_]; }
|
|
|
|
Attr & operator[](size_t pos)
|
|
{
|
|
return attrs[pos];
|
|
}
|
|
|
|
void sort();
|
|
|
|
size_t capacity() { return capacity_; }
|
|
|
|
/* Returns the attributes in lexicographically sorted order. */
|
|
std::vector<const Attr *> lexicographicOrder(const SymbolTable & symbols) const
|
|
{
|
|
std::vector<const Attr *> res;
|
|
res.reserve(size_);
|
|
for (size_t n = 0; n < size_; n++)
|
|
res.emplace_back(&attrs[n]);
|
|
std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
|
|
std::string_view sa = symbols[a->name], sb = symbols[b->name];
|
|
return sa < sb;
|
|
});
|
|
return res;
|
|
}
|
|
|
|
friend class EvalState;
|
|
};
|
|
|
|
/* A wrapper around Bindings that ensures that its always in sorted
|
|
order at the end. The only way to consume a BindingsBuilder is to
|
|
call finish(), which sorts the bindings. */
|
|
class BindingsBuilder
|
|
{
|
|
Bindings * bindings;
|
|
|
|
public:
|
|
// needed by std::back_inserter
|
|
using value_type = Attr;
|
|
|
|
EvalState & state;
|
|
|
|
BindingsBuilder(EvalState & state, Bindings * bindings)
|
|
: bindings(bindings), state(state)
|
|
{ }
|
|
|
|
void insert(SymbolIdx name, Value * value, PosIdx pos = noPos)
|
|
{
|
|
insert(Attr(name, value, pos));
|
|
}
|
|
|
|
void insert(const Attr & attr)
|
|
{
|
|
push_back(attr);
|
|
}
|
|
|
|
void push_back(const Attr & attr)
|
|
{
|
|
bindings->push_back(attr);
|
|
}
|
|
|
|
Value & alloc(const SymbolIdx & name, PosIdx pos = noPos);
|
|
|
|
Value & alloc(std::string_view name, PosIdx pos = noPos);
|
|
|
|
Bindings * finish()
|
|
{
|
|
bindings->sort();
|
|
return bindings;
|
|
}
|
|
|
|
Bindings * alreadySorted()
|
|
{
|
|
return bindings;
|
|
}
|
|
};
|
|
|
|
}
|