forked from lix-project/lix
Merge pull request #5887 from pennae/avoid-streams
avoid std::?stream overhead when it's not helpful
This commit is contained in:
commit
e61c4bc25a
|
@ -36,6 +36,19 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
static char * allocString(size_t size)
|
||||||
|
{
|
||||||
|
char * t;
|
||||||
|
#if HAVE_BOEHMGC
|
||||||
|
t = (char *) GC_MALLOC_ATOMIC(size);
|
||||||
|
#else
|
||||||
|
t = malloc(size);
|
||||||
|
#endif
|
||||||
|
if (!t) throw std::bad_alloc();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char * dupString(const char * s)
|
static char * dupString(const char * s)
|
||||||
{
|
{
|
||||||
char * t;
|
char * t;
|
||||||
|
@ -771,17 +784,28 @@ void Value::mkString(std::string_view s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void copyContextToValue(Value & v, const PathSet & context)
|
||||||
|
{
|
||||||
|
if (!context.empty()) {
|
||||||
|
size_t n = 0;
|
||||||
|
v.string.context = (const char * *)
|
||||||
|
allocBytes((context.size() + 1) * sizeof(char *));
|
||||||
|
for (auto & i : context)
|
||||||
|
v.string.context[n++] = dupString(i.c_str());
|
||||||
|
v.string.context[n] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Value::mkString(std::string_view s, const PathSet & context)
|
void Value::mkString(std::string_view s, const PathSet & context)
|
||||||
{
|
{
|
||||||
mkString(s);
|
mkString(s);
|
||||||
if (!context.empty()) {
|
copyContextToValue(*this, context);
|
||||||
size_t n = 0;
|
|
||||||
string.context = (const char * *)
|
|
||||||
allocBytes((context.size() + 1) * sizeof(char *));
|
|
||||||
for (auto & i : context)
|
|
||||||
string.context[n++] = dupString(i.c_str());
|
|
||||||
string.context[n] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Value::mkStringMove(const char * s, const PathSet & context)
|
||||||
|
{
|
||||||
|
mkString(s);
|
||||||
|
copyContextToValue(*this, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1660,13 +1684,34 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
|
||||||
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
std::ostringstream s;
|
std::vector<std::string> s;
|
||||||
|
size_t sSize = 0;
|
||||||
NixInt n = 0;
|
NixInt n = 0;
|
||||||
NixFloat nf = 0;
|
NixFloat nf = 0;
|
||||||
|
|
||||||
bool first = !forceString;
|
bool first = !forceString;
|
||||||
ValueType firstType = nString;
|
ValueType firstType = nString;
|
||||||
|
|
||||||
|
const auto str = [&] {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(sSize);
|
||||||
|
for (const auto & part : s) result += part;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
/* c_str() is not str().c_str() because we want to create a string
|
||||||
|
Value. allocating a GC'd string directly and moving it into a
|
||||||
|
Value lets us avoid an allocation and copy. */
|
||||||
|
const auto c_str = [&] {
|
||||||
|
char * result = allocString(sSize + 1);
|
||||||
|
char * tmp = result;
|
||||||
|
for (const auto & part : s) {
|
||||||
|
memcpy(tmp, part.c_str(), part.size());
|
||||||
|
tmp += part.size();
|
||||||
|
}
|
||||||
|
*tmp = 0;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
for (auto & [i_pos, i] : *es) {
|
for (auto & [i_pos, i] : *es) {
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
i->eval(state, env, vTmp);
|
i->eval(state, env, vTmp);
|
||||||
|
@ -1696,11 +1741,15 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
||||||
} else
|
} else {
|
||||||
|
if (s.empty()) s.reserve(es->size());
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
canonized in the first place if it's coming from a ./${foo} type
|
canonized in the first place if it's coming from a ./${foo} type
|
||||||
path */
|
path */
|
||||||
s << state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
|
s.emplace_back(
|
||||||
|
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
|
||||||
|
sSize += s.back().size();
|
||||||
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
@ -1712,9 +1761,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
else if (firstType == nPath) {
|
else if (firstType == nPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||||
v.mkPath(canonPath(s.str()));
|
v.mkPath(canonPath(str()));
|
||||||
} else
|
} else
|
||||||
v.mkString(s.str(), context);
|
v.mkStringMove(c_str(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -241,6 +241,8 @@ public:
|
||||||
|
|
||||||
void mkString(std::string_view s, const PathSet & context);
|
void mkString(std::string_view s, const PathSet & context);
|
||||||
|
|
||||||
|
void mkStringMove(const char * s, const PathSet & context);
|
||||||
|
|
||||||
inline void mkString(const Symbol & s)
|
inline void mkString(const Symbol & s)
|
||||||
{
|
{
|
||||||
mkString(((const std::string &) s).c_str());
|
mkString(((const std::string &) s).c_str());
|
||||||
|
|
|
@ -7,16 +7,38 @@ namespace nix {
|
||||||
|
|
||||||
void toJSON(std::ostream & str, const char * start, const char * end)
|
void toJSON(std::ostream & str, const char * start, const char * end)
|
||||||
{
|
{
|
||||||
str << '"';
|
constexpr size_t BUF_SIZE = 4096;
|
||||||
for (auto i = start; i != end; i++)
|
char buf[BUF_SIZE + 7]; // BUF_SIZE + largest single sequence of puts
|
||||||
if (*i == '\"' || *i == '\\') str << '\\' << *i;
|
size_t bufPos = 0;
|
||||||
else if (*i == '\n') str << "\\n";
|
|
||||||
else if (*i == '\r') str << "\\r";
|
const auto flush = [&] {
|
||||||
else if (*i == '\t') str << "\\t";
|
str.write(buf, bufPos);
|
||||||
else if (*i >= 0 && *i < 32)
|
bufPos = 0;
|
||||||
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec;
|
};
|
||||||
else str << *i;
|
const auto put = [&] (char c) {
|
||||||
str << '"';
|
buf[bufPos++] = c;
|
||||||
|
};
|
||||||
|
|
||||||
|
put('"');
|
||||||
|
for (auto i = start; i != end; i++) {
|
||||||
|
if (bufPos >= BUF_SIZE) flush();
|
||||||
|
if (*i == '\"' || *i == '\\') { put('\\'); put(*i); }
|
||||||
|
else if (*i == '\n') { put('\\'); put('n'); }
|
||||||
|
else if (*i == '\r') { put('\\'); put('r'); }
|
||||||
|
else if (*i == '\t') { put('\\'); put('t'); }
|
||||||
|
else if (*i >= 0 && *i < 32) {
|
||||||
|
const char hex[17] = "0123456789abcdef";
|
||||||
|
put('\\');
|
||||||
|
put('u');
|
||||||
|
put(hex[(uint16_t(*i) >> 12) & 0xf]);
|
||||||
|
put(hex[(uint16_t(*i) >> 8) & 0xf]);
|
||||||
|
put(hex[(uint16_t(*i) >> 4) & 0xf]);
|
||||||
|
put(hex[(uint16_t(*i) >> 0) & 0xf]);
|
||||||
|
}
|
||||||
|
else put(*i);
|
||||||
|
}
|
||||||
|
put('"');
|
||||||
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void toJSON(std::ostream & str, const char * s)
|
void toJSON(std::ostream & str, const char * s)
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -419,21 +421,21 @@ bool statusOk(int status);
|
||||||
|
|
||||||
/* Parse a string into an integer. */
|
/* Parse a string into an integer. */
|
||||||
template<class N>
|
template<class N>
|
||||||
std::optional<N> string2Int(const std::string & s)
|
std::optional<N> string2Int(const std::string_view s)
|
||||||
{
|
{
|
||||||
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
std::istringstream str(s);
|
try {
|
||||||
N n;
|
return boost::lexical_cast<N>(s.data(), s.size());
|
||||||
str >> n;
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
if (str && str.get() == EOF) return n;
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
||||||
'T' denoting a binary unit prefix. */
|
'T' denoting a binary unit prefix. */
|
||||||
template<class N>
|
template<class N>
|
||||||
N string2IntWithUnitPrefix(std::string s)
|
N string2IntWithUnitPrefix(std::string_view s)
|
||||||
{
|
{
|
||||||
N multiplier = 1;
|
N multiplier = 1;
|
||||||
if (!s.empty()) {
|
if (!s.empty()) {
|
||||||
|
@ -444,7 +446,7 @@ N string2IntWithUnitPrefix(std::string s)
|
||||||
else if (u == 'G') multiplier = 1ULL << 30;
|
else if (u == 'G') multiplier = 1ULL << 30;
|
||||||
else if (u == 'T') multiplier = 1ULL << 40;
|
else if (u == 'T') multiplier = 1ULL << 40;
|
||||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||||
s.resize(s.size() - 1);
|
s.remove_suffix(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto n = string2Int<N>(s))
|
if (auto n = string2Int<N>(s))
|
||||||
|
@ -454,14 +456,14 @@ N string2IntWithUnitPrefix(std::string s)
|
||||||
|
|
||||||
/* Parse a string into a float. */
|
/* Parse a string into a float. */
|
||||||
template<class N>
|
template<class N>
|
||||||
std::optional<N> string2Float(const string & s)
|
std::optional<N> string2Float(const std::string_view s)
|
||||||
{
|
{
|
||||||
std::istringstream str(s);
|
try {
|
||||||
N n;
|
return boost::lexical_cast<N>(s.data(), s.size());
|
||||||
str >> n;
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
if (str && str.get() == EOF) return n;
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return true iff `s' starts with `prefix'. */
|
/* Return true iff `s' starts with `prefix'. */
|
||||||
|
|
Loading…
Reference in a new issue