Skip to content

Commit cbe8ec7

Browse files
authored
Merge pull request #14470 from NixOS/ctx-type-alias
Encapsulate and slightly optimize string contexts
2 parents 60667e9 + 318eea0 commit cbe8ec7

File tree

4 files changed

+97
-23
lines changed

4 files changed

+97
-23
lines changed

src/libexpr/eval-cache.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,19 @@ struct AttrDb
136136
});
137137
}
138138

139-
AttrId setString(AttrKey key, std::string_view s, const char ** context = nullptr)
139+
AttrId setString(AttrKey key, std::string_view s, const Value::StringWithContext::Context * context = nullptr)
140140
{
141141
return doSQLite([&]() {
142142
auto state(_state->lock());
143143

144144
if (context) {
145145
std::string ctx;
146-
for (const char ** p = context; *p; ++p) {
147-
if (p != context)
146+
bool first = true;
147+
for (auto * elem : *context) {
148+
if (!first)
148149
ctx.push_back(' ');
149-
ctx.append(*p);
150+
ctx.append(elem);
151+
first = false;
150152
}
151153
state->insertAttributeWithContext.use()(key.first)(symbols[key.second])(AttrType::String) (s) (ctx)
152154
.exec();

src/libexpr/eval.cc

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <sys/time.h>
3737
#include <fstream>
3838
#include <functional>
39+
#include <ranges>
3940

4041
#include <nlohmann/json.hpp>
4142
#include <boost/container/small_vector.hpp>
@@ -821,28 +822,25 @@ void Value::mkString(std::string_view s)
821822
mkStringNoCopy(makeImmutableString(s));
822823
}
823824

824-
static const char ** encodeContext(const NixStringContext & context)
825+
Value::StringWithContext::Context * Value::StringWithContext::Context::fromBuilder(const NixStringContext & context)
825826
{
826-
if (!context.empty()) {
827-
size_t n = 0;
828-
auto ctx = (const char **) allocBytes((context.size() + 1) * sizeof(char *));
829-
for (auto & i : context) {
830-
ctx[n++] = makeImmutableString({i.to_string()});
831-
}
832-
ctx[n] = nullptr;
833-
return ctx;
834-
} else
827+
if (context.empty())
835828
return nullptr;
829+
830+
auto ctx = new (allocBytes(sizeof(Context) + context.size() * sizeof(value_type))) Context(context.size());
831+
std::ranges::transform(
832+
context, ctx->elems, [](const NixStringContextElem & elt) { return makeImmutableString(elt.to_string()); });
833+
return ctx;
836834
}
837835

838836
void Value::mkString(std::string_view s, const NixStringContext & context)
839837
{
840-
mkStringNoCopy(makeImmutableString(s), encodeContext(context));
838+
mkStringNoCopy(makeImmutableString(s), Value::StringWithContext::Context::fromBuilder(context));
841839
}
842840

843841
void Value::mkStringMove(const char * s, const NixStringContext & context)
844842
{
845-
mkStringNoCopy(s, encodeContext(context));
843+
mkStringNoCopy(s, Value::StringWithContext::Context::fromBuilder(context));
846844
}
847845

848846
void Value::mkPath(const SourcePath & path)
@@ -2288,9 +2286,9 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
22882286

22892287
void copyContext(const Value & v, NixStringContext & context, const ExperimentalFeatureSettings & xpSettings)
22902288
{
2291-
if (v.context())
2292-
for (const char ** p = v.context(); *p; ++p)
2293-
context.insert(NixStringContextElem::parse(*p, xpSettings));
2289+
if (auto * ctx = v.context())
2290+
for (auto * elem : *ctx)
2291+
context.insert(NixStringContextElem::parse(elem, xpSettings));
22942292
}
22952293

22962294
std::string_view EvalState::forceString(
@@ -2310,7 +2308,9 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
23102308
auto s = forceString(v, pos, errorCtx);
23112309
if (v.context()) {
23122310
error<EvalError>(
2313-
"the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string_view(), v.context()[0])
2311+
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
2312+
v.string_view(),
2313+
*v.context()->begin())
23142314
.withTrace(pos, errorCtx)
23152315
.debugThrow();
23162316
}

src/libexpr/include/nix/expr/value.hh

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,66 @@ struct ValueBase
220220
struct StringWithContext
221221
{
222222
const char * c_str;
223-
const char ** context; // must be in sorted order
223+
224+
/**
225+
* The type of the context itself.
226+
*
227+
* Currently, it is length-prefixed array of pointers to
228+
* null-terminated strings. The strings are specially formatted
229+
* to represent a flattening of the recursive sum type that is a
230+
* context element.
231+
*
232+
* @See NixStringContext for an more easily understood type,
233+
* that of the "builder" for this data structure.
234+
*/
235+
struct Context
236+
{
237+
using value_type = const char *;
238+
using size_type = std::size_t;
239+
using iterator = const value_type *;
240+
241+
Context(size_type size)
242+
: size_(size)
243+
{
244+
}
245+
246+
private:
247+
/**
248+
* Number of items in the array
249+
*/
250+
size_type size_;
251+
252+
/**
253+
* @pre must be in sorted order
254+
*/
255+
value_type elems[];
256+
257+
public:
258+
iterator begin() const
259+
{
260+
return elems;
261+
}
262+
263+
iterator end() const
264+
{
265+
return elems + size();
266+
}
267+
268+
size_type size() const
269+
{
270+
return size_;
271+
}
272+
273+
/**
274+
* @return null pointer when context.empty()
275+
*/
276+
static Context * fromBuilder(const NixStringContext & context);
277+
};
278+
279+
/**
280+
* May be null for a string without context.
281+
*/
282+
const Context * context;
224283
};
225284

226285
struct Path
@@ -991,7 +1050,7 @@ public:
9911050
setStorage(b);
9921051
}
9931052

994-
void mkStringNoCopy(const char * s, const char ** context = 0) noexcept
1053+
void mkStringNoCopy(const char * s, const Value::StringWithContext::Context * context = nullptr) noexcept
9951054
{
9961055
setStorage(StringWithContext{.c_str = s, .context = context});
9971056
}
@@ -1117,7 +1176,7 @@ public:
11171176
return getStorage<StringWithContext>().c_str;
11181177
}
11191178

1120-
const char ** context() const noexcept
1179+
const Value::StringWithContext::Context * context() const noexcept
11211180
{
11221181
return getStorage<StringWithContext>().context;
11231182
}

src/libexpr/include/nix/expr/value/context.hh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ public:
2424
}
2525
};
2626

27+
/**
28+
* @todo This should be renamed to `StringContextBuilderElem`, since:
29+
*
30+
* 1. We use `*Builder` for off-heap temporary data structures
31+
*
32+
* 2. The `Nix*` is totally redundant. (And my mistake from a long time
33+
* ago.)
34+
*/
2735
struct NixStringContextElem
2836
{
2937
/**
@@ -77,6 +85,11 @@ struct NixStringContextElem
7785
std::string to_string() const;
7886
};
7987

88+
/**
89+
* @todo This should be renamed to `StringContextBuilder`.
90+
*
91+
* @see NixStringContextElem for explanation why.
92+
*/
8093
typedef std::set<NixStringContextElem> NixStringContext;
8194

8295
} // namespace nix

0 commit comments

Comments
 (0)