forked from lix-project/lix
Qyriad
72ee25b402
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
150 lines
4.2 KiB
C++
150 lines
4.2 KiB
C++
#pragma once
|
|
/// @file Aliases and wrapper functions that are transparently GC-enabled
|
|
/// if Lix is compiled with BoehmGC enabled.
|
|
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <list>
|
|
#include <map>
|
|
#include <new>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#if HAVE_BOEHMGC
|
|
#include <functional> // std::less
|
|
#include <utility> // std::pair
|
|
#define GC_INCLUDE_NEW
|
|
#include <gc/gc.h>
|
|
#include <gc/gc_allocator.h>
|
|
#include <gc/gc_cpp.h>
|
|
|
|
#include "checked-arithmetic.hh"
|
|
|
|
/// calloc, transparently GC-enabled.
|
|
#define LIX_GC_CALLOC(size) GC_MALLOC(size)
|
|
|
|
/// strdup, transaprently GC-enabled.
|
|
#define LIX_GC_STRDUP(str) GC_STRDUP(str)
|
|
|
|
/// Atomic GC malloc() with GC enabled, or regular malloc() otherwise.
|
|
#define LIX_GC_MALLOC_ATOMIC(size) GC_MALLOC_ATOMIC(size)
|
|
|
|
namespace nix
|
|
{
|
|
|
|
/// Alias for std::map which uses BoehmGC's allocator conditional on this Lix
|
|
/// build having GC enabled.
|
|
template<typename KeyT, typename ValueT>
|
|
using GcMap = std::map<
|
|
KeyT,
|
|
ValueT,
|
|
std::less<KeyT>,
|
|
traceable_allocator<std::pair<KeyT const, ValueT>>
|
|
>;
|
|
|
|
/// Alias for std::vector which uses BoehmGC's allocator conditional on this Lix
|
|
/// build having GC enabled.
|
|
template<typename ItemT>
|
|
using GcVector = std::vector<ItemT, traceable_allocator<ItemT>>;
|
|
|
|
/// Alias for std::list which uses BoehmGC's allocator conditional on this Lix
|
|
/// build having GC enabled.
|
|
template<typename ItemT>
|
|
using GcList = std::list<ItemT, traceable_allocator<ItemT>>;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#include <cstdlib>
|
|
|
|
/// calloc, transparently GC-enabled.
|
|
#define LIX_GC_CALLOC(size) calloc(size, 1)
|
|
|
|
/// strdup, transparently GC-enabled.
|
|
#define LIX_GC_STRDUP(str) strdup(str)
|
|
|
|
/// Atomic GC malloc() with GC enabled, or regular malloc() otherwise.
|
|
/// The returned memory must never contain pointers.
|
|
#define LIX_GC_MALLOC_ATOMIC(size) malloc(size)
|
|
|
|
namespace nix
|
|
{
|
|
|
|
/// Alias for std::map which uses BoehmGC's allocator conditional on this Lix
|
|
/// build having GC enabled.
|
|
template<typename KeyT, typename ValueT>
|
|
using GcMap = std::map<KeyT, ValueT>;
|
|
|
|
/// Alias for std::vector which uses BoehmGC's allocator conditional on this Lix
|
|
/// build having GC enabled.
|
|
template<typename ItemT>
|
|
using GcVector = std::vector<ItemT>;
|
|
|
|
/// Alias for std::list which uses BoehmGC's allocator conditional on this Lix
|
|
/// build having GC enabled.
|
|
template<typename ItemT>
|
|
using GcList = std::list<ItemT>;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
namespace nix
|
|
{
|
|
|
|
[[gnu::always_inline]]
|
|
inline void * gcAllocBytes(size_t n)
|
|
{
|
|
// Note: various places expect the allocated memory to be zero.
|
|
// Hence: calloc().
|
|
void * ptr = LIX_GC_CALLOC(n);
|
|
if (ptr == nullptr) {
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
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*,
|
|
/// meaning you should include the size needed by the NUL terminator in the
|
|
/// passed size. Memory allocated with this function must never contain other
|
|
/// pointers.
|
|
inline char * gcAllocString(size_t size)
|
|
{
|
|
char * cstr = static_cast<char *>(LIX_GC_MALLOC_ATOMIC(size));
|
|
if (cstr == nullptr) {
|
|
throw std::bad_alloc();
|
|
}
|
|
return cstr;
|
|
}
|
|
|
|
/// Returns a C-string copied from @ref toCopyFrom, or a single, static empty
|
|
/// string if @ref toCopyFrom is also empty.
|
|
char const * gcCopyStringIfNeeded(std::string_view toCopyFrom);
|
|
|
|
}
|