mirror of
https://github.com/NixOS/nix.git
synced 2025-11-08 19:46:02 +01:00
Encapsulate and slightly optimize string contexts
These steps are done (originally in order, but I squashed it as the end result is still pretty small, and the churn in the code comments was a bit annoying to keep straight). 1. Create proper struct type for string contexts on the heap This will make it easier to change this type in the future. 2. Make `Value::StringWithContext` iterable This make some for loops a lot more terse. 3. Encapsulate `Value::StringWithContext::Context::elems` It turns out the iterators we just exposed are sufficient. 4. Make `StringWithContext::Context` length-prefixed instead Rather than having a null pointer at the end, have a `size_t` at the beginning. This is the exact same size (note that null pointer is longer than null byte) and thus takes no more space! Also, see the new TODO on naming. The thing we already so-named is a builder type for string contexts, not the on-heap type. The `fromBuilder` static method reflects what the names ought to be too.
This commit is contained in:
parent
5b15544bdd
commit
e6afa20c67
4 changed files with 87 additions and 19 deletions
|
|
@ -136,17 +136,19 @@ struct AttrDb
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
AttrId setString(AttrKey key, std::string_view s, const char ** context = nullptr)
|
AttrId setString(AttrKey key, std::string_view s, const Value::StringWithContext::Context * context = nullptr)
|
||||||
{
|
{
|
||||||
return doSQLite([&]() {
|
return doSQLite([&]() {
|
||||||
auto state(_state->lock());
|
auto state(_state->lock());
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
std::string ctx;
|
std::string ctx;
|
||||||
for (const char ** p = context; *p; ++p) {
|
bool first = true;
|
||||||
if (p != context)
|
for (auto * elem : *context) {
|
||||||
|
if (!first)
|
||||||
ctx.push_back(' ');
|
ctx.push_back(' ');
|
||||||
ctx.append(*p);
|
ctx.append(elem);
|
||||||
|
first = false;
|
||||||
}
|
}
|
||||||
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
|
||||||
.exec();
|
.exec();
|
||||||
|
|
|
||||||
|
|
@ -821,15 +821,18 @@ void Value::mkString(std::string_view s)
|
||||||
mkStringNoCopy(makeImmutableString(s));
|
mkStringNoCopy(makeImmutableString(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char ** encodeContext(const NixStringContext & context)
|
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
|
||||||
{
|
{
|
||||||
if (!context.empty()) {
|
if (!context.empty()) {
|
||||||
size_t n = 0;
|
auto ctx = (Value::StringWithContext::Context *) allocBytes(sizeof(size_t) + context.size() * sizeof(char *));
|
||||||
auto ctx = (const char **) allocBytes((context.size() + 1) * sizeof(char *));
|
ctx->size = context.size();
|
||||||
for (auto & i : context) {
|
/* Mapping the original iterator to turn references into
|
||||||
ctx[n++] = makeImmutableString({i.to_string()});
|
pointers is necessary to make sure that enumerate doesn't
|
||||||
|
accidently copy the elements when it returns tuples by value.
|
||||||
|
*/
|
||||||
|
for (auto [n, i] : enumerate(context | std::views::transform([](const auto & r) { return &r; }))) {
|
||||||
|
ctx->elems[n] = makeImmutableString({i->to_string()});
|
||||||
}
|
}
|
||||||
ctx[n] = nullptr;
|
|
||||||
return ctx;
|
return ctx;
|
||||||
} else
|
} else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -837,12 +840,12 @@ static const char ** encodeContext(const NixStringContext & context)
|
||||||
|
|
||||||
void Value::mkString(std::string_view s, const NixStringContext & context)
|
void Value::mkString(std::string_view s, const NixStringContext & context)
|
||||||
{
|
{
|
||||||
mkStringNoCopy(makeImmutableString(s), encodeContext(context));
|
mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::mkStringMove(const char * s, const NixStringContext & context)
|
void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||||
{
|
{
|
||||||
mkStringNoCopy(s, encodeContext(context));
|
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Value::mkPath(const SourcePath & path)
|
void Value::mkPath(const SourcePath & path)
|
||||||
|
|
@ -2287,9 +2290,9 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
|
||||||
|
|
||||||
void copyContext(const Value & v, NixStringContext & context, const ExperimentalFeatureSettings & xpSettings)
|
void copyContext(const Value & v, NixStringContext & context, const ExperimentalFeatureSettings & xpSettings)
|
||||||
{
|
{
|
||||||
if (v.context())
|
if (auto * ctx = v.context())
|
||||||
for (const char ** p = v.context(); *p; ++p)
|
for (auto * elem : *ctx)
|
||||||
context.insert(NixStringContextElem::parse(*p, xpSettings));
|
context.insert(NixStringContextElem::parse(elem, xpSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view EvalState::forceString(
|
std::string_view EvalState::forceString(
|
||||||
|
|
@ -2309,7 +2312,9 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
|
||||||
auto s = forceString(v, pos, errorCtx);
|
auto s = forceString(v, pos, errorCtx);
|
||||||
if (v.context()) {
|
if (v.context()) {
|
||||||
error<EvalError>(
|
error<EvalError>(
|
||||||
"the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string_view(), v.context()[0])
|
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||||
|
v.string_view(),
|
||||||
|
*v.context()->begin())
|
||||||
.withTrace(pos, errorCtx)
|
.withTrace(pos, errorCtx)
|
||||||
.debugThrow();
|
.debugThrow();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,55 @@ struct ValueBase
|
||||||
struct StringWithContext
|
struct StringWithContext
|
||||||
{
|
{
|
||||||
const char * c_str;
|
const char * c_str;
|
||||||
const char ** context; // must be in sorted order
|
|
||||||
|
/**
|
||||||
|
* The type of the context itself.
|
||||||
|
*
|
||||||
|
* Currently, it is length-prefixed array of pointers to
|
||||||
|
* null-terminated strings. The strings are specially formatted
|
||||||
|
* to represent a flattening of the recursive sum type that is a
|
||||||
|
* context element.
|
||||||
|
*
|
||||||
|
* @See NixStringContext for an more easily understood type,
|
||||||
|
* that of the "builder" for this data structure.
|
||||||
|
*/
|
||||||
|
struct Context
|
||||||
|
{
|
||||||
|
using Elem = const char *;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of items in the array
|
||||||
|
*/
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* must be in sorted order
|
||||||
|
*/
|
||||||
|
Elem elems[/*size*/];
|
||||||
|
public:
|
||||||
|
|
||||||
|
const Elem * begin() const
|
||||||
|
{
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Elem * end() const
|
||||||
|
{
|
||||||
|
return &elems[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note returns a null pointer to more concisely encode the
|
||||||
|
* empty context
|
||||||
|
*/
|
||||||
|
static Context * fromBuilder(const NixStringContext & context);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* May be null for a string without context.
|
||||||
|
*/
|
||||||
|
const Context * context;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Path
|
struct Path
|
||||||
|
|
@ -991,7 +1039,7 @@ public:
|
||||||
setStorage(b);
|
setStorage(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mkStringNoCopy(const char * s, const char ** context = 0) noexcept
|
void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
|
||||||
{
|
{
|
||||||
setStorage(StringWithContext{.c_str = s, .context = context});
|
setStorage(StringWithContext{.c_str = s, .context = context});
|
||||||
}
|
}
|
||||||
|
|
@ -1117,7 +1165,7 @@ public:
|
||||||
return getStorage<StringWithContext>().c_str;
|
return getStorage<StringWithContext>().c_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char ** context() const noexcept
|
const Value::StringWithContext::Context * context() const noexcept
|
||||||
{
|
{
|
||||||
return getStorage<StringWithContext>().context;
|
return getStorage<StringWithContext>().context;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,14 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo This should be reamed to `StringContextBuilderElem`, since:
|
||||||
|
*
|
||||||
|
* 1. We use `*Builder` for off-heap temporary data structures
|
||||||
|
*
|
||||||
|
* 2. The `Nix*` is totally redundant. (And my mistake from a long time
|
||||||
|
* ago.)
|
||||||
|
*/
|
||||||
struct NixStringContextElem
|
struct NixStringContextElem
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,6 +85,11 @@ struct NixStringContextElem
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo This should be renamed to `StringContextBuilder`.
|
||||||
|
*
|
||||||
|
* @see NixStringContextElem for explanation why.
|
||||||
|
*/
|
||||||
typedef std::set<NixStringContextElem> NixStringContext;
|
typedef std::set<NixStringContextElem> NixStringContext;
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue