Nix 2.93.3
Lix: A modern, delicious implementation of the Nix package manager; unstable internal interfaces
Loading...
Searching...
No Matches
gc-alloc.hh
1#pragma once
4
5#include <cstddef>
6#include <cstring>
7#include <list>
8#include <map>
9#include <new>
10#include <string_view>
11#include <vector>
12
13#include "lix/libutil/checked-arithmetic.hh"
14
15#if HAVE_BOEHMGC
16#define GC_INCLUDE_NEW
17#include <gc/gc.h>
18#include <gc/gc_allocator.h>
19#include <gc/gc_cpp.h>
20
22#define LIX_GC_CALLOC(size) GC_MALLOC(size)
23
25#define LIX_GC_STRDUP(str) GC_STRDUP(str)
26
28#define LIX_GC_MALLOC_ATOMIC(size) GC_MALLOC_ATOMIC(size)
29
30namespace nix
31{
32
33template<typename T>
34using TraceableAllocator = traceable_allocator<T>;
35
36}
37
38#else
39
40#include <cstdlib>
41
43#define LIX_GC_CALLOC(size) calloc(size, 1)
44
46#define LIX_GC_STRDUP(str) strdup(str)
47
50#define LIX_GC_MALLOC_ATOMIC(size) malloc(size)
51
52namespace nix
53{
54
55template<typename T>
56using TraceableAllocator = std::allocator<T>;
57
58}
59
60#endif
61
62namespace nix
63{
64
67template<typename KeyT, typename ValueT>
68using GcMap = std::map<
69 KeyT,
70 ValueT,
71 std::less<KeyT>,
72 TraceableAllocator<std::pair<KeyT const, ValueT>>
73>;
74
77template<typename ItemT>
78using GcVector = std::vector<ItemT, TraceableAllocator<ItemT>>;
79
82template<typename ItemT>
83using GcList = std::list<ItemT, TraceableAllocator<ItemT>>;
84
85[[gnu::always_inline]]
86inline void * gcAllocBytes(size_t n)
87{
88 // Note: various places expect the allocated memory to be zero.
89 // Hence: calloc().
90 void * ptr = LIX_GC_CALLOC(n);
91 if (ptr == nullptr) {
92 throw std::bad_alloc();
93 }
94
95 return ptr;
96}
97
102template<typename T>
103[[gnu::always_inline]]
104inline T * gcAllocType(size_t howMany = 1)
105{
106 // NOTE: size_t * size_t, which can definitely overflow.
107 // Unsigned integer overflow is definitely a bug, but isn't undefined
108 // behavior, so we can just check if we overflowed after the fact.
109 // However, people can and do request zero sized allocations, so we need
110 // to check that neither of our multiplicands were zero before complaining
111 // about it.
112 // NOLINTNEXTLINE(bugprone-sizeof-expression): yeah we only seem to alloc pointers with this. the calculation *is* correct though!
113 auto checkedSz = checked::Checked<size_t>(howMany) * sizeof(T);
114 size_t sz = checkedSz.valueWrapping();
115 if (checkedSz.overflowed()) {
116 // Congrats, you done did an overflow.
117 throw std::bad_alloc();
118 }
119
120 return static_cast<T *>(gcAllocBytes(sz));
121}
122
127inline char * gcAllocString(size_t size)
128{
129 char * cstr = static_cast<char *>(LIX_GC_MALLOC_ATOMIC(size));
130 if (cstr == nullptr) {
131 throw std::bad_alloc();
132 }
133 return cstr;
134}
135
138char const * gcCopyStringIfNeeded(std::string_view toCopyFrom);
139
140}
Definition checked-arithmetic.hh:25