forked from lix-project/lix
libexpr: add a strongly typed version of gcAllocBytes()
This commit adds a new helper template function to gc-alloc.hh (which is
probably where you want to look at first, O great reviewer [custom file
ordering in review diffs when]), which uses a type argument to determine
the size to allocate, rather than making the caller use sizeof().
Change-Id: Ib5d138d91a28bdda304a80db24ea9fb08669ad22
This commit is contained in:
parent
e67dac1d74
commit
72ee25b402
|
@ -777,8 +777,7 @@ static void copyContextToValue(Value & v, const NixStringContext & context)
|
||||||
{
|
{
|
||||||
if (!context.empty()) {
|
if (!context.empty()) {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
v.string.context = (const char * *)
|
v.string.context = gcAllocType<char const *>(context.size() + 1);
|
||||||
gcAllocBytes((context.size() + 1) * sizeof(char *));
|
|
||||||
for (auto & i : context)
|
for (auto & i : context)
|
||||||
v.string.context[n++] = gcCopyStringIfNeeded(i.to_string());
|
v.string.context[n++] = gcCopyStringIfNeeded(i.to_string());
|
||||||
v.string.context[n] = 0;
|
v.string.context[n] = 0;
|
||||||
|
@ -834,7 +833,7 @@ void EvalState::mkList(Value & v, size_t size)
|
||||||
{
|
{
|
||||||
v.mkList(size);
|
v.mkList(size);
|
||||||
if (size > 2)
|
if (size > 2)
|
||||||
v.bigList.elems = (Value * *) gcAllocBytes(size * sizeof(Value *));
|
v.bigList.elems = gcAllocType<Value *>(size);
|
||||||
nrListElems += size;
|
nrListElems += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <gc/gc_allocator.h>
|
#include <gc/gc_allocator.h>
|
||||||
#include <gc/gc_cpp.h>
|
#include <gc/gc_cpp.h>
|
||||||
|
|
||||||
|
#include "checked-arithmetic.hh"
|
||||||
|
|
||||||
/// calloc, transparently GC-enabled.
|
/// calloc, transparently GC-enabled.
|
||||||
#define LIX_GC_CALLOC(size) GC_MALLOC(size)
|
#define LIX_GC_CALLOC(size) GC_MALLOC(size)
|
||||||
|
|
||||||
|
@ -104,6 +106,30 @@ inline void * gcAllocBytes(size_t n)
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Typed, safe wrapper around calloc() (transparently GC-enabled). Allocates
|
||||||
|
/// enough for the requested count of the specified type. Also checks for
|
||||||
|
/// nullptr (and throws @ref std::bad_alloc), and casts the void pointer to
|
||||||
|
/// a pointer of the specified type, for type-convenient goodness.
|
||||||
|
template<typename T>
|
||||||
|
[[gnu::always_inline]]
|
||||||
|
inline T * gcAllocType(size_t howMany = 1)
|
||||||
|
{
|
||||||
|
// NOTE: size_t * size_t, which can definitely overflow.
|
||||||
|
// Unsigned integer overflow is definitely a bug, but isn't undefined
|
||||||
|
// behavior, so we can just check if we overflowed after the fact.
|
||||||
|
// However, people can and do request zero sized allocations, so we need
|
||||||
|
// to check that neither of our multiplicands were zero before complaining
|
||||||
|
// about it.
|
||||||
|
auto checkedSz = checked::Checked<size_t>(howMany) * sizeof(T);
|
||||||
|
size_t sz = checkedSz.valueWrapping();
|
||||||
|
if (checkedSz.overflowed()) {
|
||||||
|
// Congrats, you done did an overflow.
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<T *>(gcAllocBytes(sz));
|
||||||
|
}
|
||||||
|
|
||||||
/// GC-transparently allocates a buffer for a C-string of @ref size *bytes*,
|
/// GC-transparently allocates a buffer for a C-string of @ref size *bytes*,
|
||||||
/// meaning you should include the size needed by the NUL terminator in the
|
/// meaning you should include the size needed by the NUL terminator in the
|
||||||
/// passed size. Memory allocated with this function must never contain other
|
/// passed size. Memory allocated with this function must never contain other
|
||||||
|
|
Loading…
Reference in a new issue