Replace withBuffer by boost small_vector

Although this will leave gaps in the stack, the performance impact
of those should be insignificant and we get a simpler solution
this way.
This commit is contained in:
Robert Hensing 2022-01-19 15:20:46 +01:00
parent 624f18ad90
commit dec7748119
2 changed files with 22 additions and 38 deletions

View file

@ -4,6 +4,7 @@
#include "util.hh" #include "util.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "fs-accessor.hh" #include "fs-accessor.hh"
#include <boost/container/small_vector.hpp>
namespace nix { namespace nix {
@ -272,19 +273,27 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
static void printString(string & res, std::string_view s) static void printString(string & res, std::string_view s)
{ {
size_t bufSize = s.size() * 2 + 2; // Large stack allocations can skip past the stack protection page.
withBuffer(bufSize, [&](char *buf) { const size_t stack_protection_size = 4096;
char * p = buf; // We reduce the max stack allocated buffer by an extra amount to increase
*p++ = '"'; // the chance of hitting it, even when `fun`'s first access is some distance
for (auto c : s) // into its *further* stack frame, particularly if the call was inlined and
if (c == '\"' || c == '\\') { *p++ = '\\'; *p++ = c; } // therefore not writing a frame pointer.
else if (c == '\n') { *p++ = '\\'; *p++ = 'n'; } const size_t play = 64 * sizeof(char *); // 512B on 64b archs
else if (c == '\r') { *p++ = '\\'; *p++ = 'r'; }
else if (c == '\t') { *p++ = '\\'; *p++ = 't'; } boost::container::small_vector<char, stack_protection_size - play> buffer;
else *p++ = c; buffer.reserve(s.size() * 2 + 2);
*p++ = '"'; char * buf = buffer.data();
res.append(buf, p - buf); char * p = buf;
}); *p++ = '"';
for (auto c : s)
if (c == '\"' || c == '\\') { *p++ = '\\'; *p++ = c; }
else if (c == '\n') { *p++ = '\\'; *p++ = 'n'; }
else if (c == '\r') { *p++ = '\\'; *p++ = 'r'; }
else if (c == '\t') { *p++ = '\\'; *p++ = 't'; }
else *p++ = c;
*p++ = '"';
res.append(buf, p - buf);
} }

View file

@ -671,30 +671,5 @@ template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
std::string showBytes(uint64_t bytes); std::string showBytes(uint64_t bytes);
/**
`withBuffer(size, fun)` applies `fun` to a temporary `char` array of size `size`.
The array will be allocated either on the stack or on the heap depending on its size
*/
template<typename T = char, typename Fn>
inline auto withBuffer(size_t n, Fn fun)
-> std::invoke_result_t<Fn, T *>
{
// Large stack allocations can skip past the stack protection page.
const size_t stack_protection_size = 4096;
// We reduce the max stack allocated buffer by an extra amount to increase
// the chance of hitting it, even when `fun`'s first access is some distance
// into its *further* stack frame, particularly if the call was inlined and
// therefore not writing a frame pointer.
const size_t play = 64 * sizeof(char *); // 512B on 64b archs
size_t size_bytes = n * sizeof(T);
if (size_bytes < stack_protection_size - play) {
T buf[n];
return fun(buf);
} else {
auto buf = std::unique_ptr<T[]>(new T[n]);
return fun(buf.get());
}
}
} }