#Header
#include "co/mem.h"
The API is in the co namespace.
APIs starting with an underscore are generally used internally by coost and are not recommended for users to call.
#Overview
The coost memory allocator divides memory into three categories:
| Category | Size Range | Alignment |
|---|---|---|
| Small memory | <= S (S is less than 4K) |
16-byte aligned |
| Medium memory | <= 128K |
4K aligned |
| Large memory | > 128K |
Page-aligned, directly mmap / VirtualAlloc |
Unlike ordinary memory allocators, the coost allocator does not store the size of allocated memory; free requires passing the size.
This design has the following benefits:
- The allocator does not need to write metadata at the beginning of memory; the memory layout obtained by users is compact, with no extra overhead;
- During free, the category can be determined directly from the size without reading metadata, making the path simple;
- It is cache-friendly, and allocation and deallocation are faster.
#Basic Allocation Functions
void* co::alloc(size_t n);
// @align: must be power of 2, and its maximum value is 256
void* co::alloc(size_t n, size_t align);
// @p: may be NULL
// @n: MUST be the same as the size used in alloc or realloc
void co::free(void* p, size_t n);
// @p: may be NULL
// @o: old size, must be the same as the size used in alloc or realloc
// @n: new size, must be greater than @o
// return: may be the same as @p, or NULL on failure
void* co::realloc(void* p, size_t o, size_t n);
// alloc and zero-clear
void* co::zalloc(size_t n);
void* co::zalloc(size_t n, size_t align);
// virtual alloc, page-aligned and zero-cleared
void* co::valloc(size_t n);
void co::vfree(void* p, size_t n);
char* co::strdup(const char* s);
- The above APIs are all thread-safe.
- In
co::free(p, n),nmust be the same as at allocation time; otherwise, undefined behavior may result. co::realloc(p, o, n)requiresn > o, i.e. it can only expand; on failure it returnsnullptr, and the originalpis unaffected and remains valid.zallocallocates memory and zeroes it.allocandzallocsupport allocating memory aligned toalign;alignmust be a power of 2, and the maximum allowed value is 256.vallocallocates page-aligned memory with contents zeroed; invfree,nmust be the same as at allocation time.strdupcreates a string copy; when releasing it, useco::free(s, strlen(s) + 1):
Memory allocated by valloc does not support realloc; it is usually used to allocate large blocks of memory that do not need realloc.
#Static Object Construction
// make static object, which will be destructed automatically at exit
template<typename T, typename... Args>
inline T* co::make_static(Args&&... args);
// make non-dependent static object at the root level
template<typename T, typename... Args>
inline T* co::make_rootic(Args&&... args);
- Creates a static object; the returned pointer must not be manually freed or deleted by the user.
- For objects created by
co::make_static, coost destroys them at program exit in the order of first constructed, later destroyed. - Objects created by
co::make_rooticare always destroyed last; it is generally used to create dependency-free static objects.
Example:
co::string* g_s = co::make_static<co::string>(32, 'x');
#co::unique
template<typename T>
struct unique {
constexpr unique() noexcept;
constexpr unique(std::nullptr_t) noexcept;
unique(unique& x) noexcept;
unique(unique&& x) noexcept;
~unique();
unique(const unique&) = delete;
unique& operator=(unique&& x) noexcept;
unique& operator=(unique& x) noexcept;
// Cross-type conversion: requires T to be a base class of X, and T has a virtual destructor
template<typename X> unique(unique<X>& x) noexcept;
template<typename X> unique(unique<X>&& x) noexcept;
template<typename X> unique& operator=(unique<X>&& x) noexcept;
template<typename X> unique& operator=(unique<X>& x) noexcept;
T* get() const noexcept;
T* operator->() const noexcept; // runtime_assert(_p)
T& operator*() const noexcept; // runtime_assert(_p)
bool operator==(T* p) const noexcept;
bool operator!=(T* p) const noexcept;
explicit operator bool() const noexcept;
void reset() noexcept;
void swap(unique& x) noexcept;
void swap(unique&& x) noexcept;
union { T* _p; uint32* _s; };
};
template<typename T, typename... Args>
inline unique<T> co::make_unique(Args&&... args);
- Similar to
std::unique_ptr. - Only move semantics are supported, ensuring the object is always managed by a unique
unique.
Example:
co::unique<co::string> s = co::make_unique<co::string>(32, 'x');
co::println("*s = ", *s);
// move semantics, s -> nullptr
co::unique<co::string> x = s;
#co::shared
template<typename T>
struct shared {
constexpr shared() noexcept;
constexpr shared(std::nullptr_t) noexcept;
shared(const shared& x) noexcept;
shared(shared&& x) noexcept;
~shared();
shared& operator=(const shared& x) noexcept;
shared& operator=(shared&& x) noexcept;
// Cross-type conversion: requires T to be a base class of X, and T has a virtual destructor
template<typename X> shared(const shared<X>& x) noexcept;
template<typename X> shared(shared<X>&& x) noexcept;
template<typename X> shared& operator=(const shared<X>& x) noexcept;
template<typename X> shared& operator=(shared<X>&& x) noexcept;
T* get() const noexcept;
T* operator->() const noexcept;
T& operator*() const noexcept;
bool operator==(T* p) const noexcept;
bool operator!=(T* p) const noexcept;
explicit operator bool() const noexcept;
void reset() noexcept;
size_t ref_count() const noexcept;
size_t use_count() const noexcept;
void swap(shared& x) noexcept;
void swap(shared&& x) noexcept;
union { T* _p; uint32* _s; };
};
template<typename T, typename... Args>
inline shared<T> co::make_shared(Args&&... args);
- Similar to
std::shared_ptr. - If the internally held pointer is
nullptr, copying does not increase the reference count.
Example:
co::shared<co::string> s = co::make_shared<co::string>(32, 'x');
co::println("use_count = ", s.use_count()); // 1
// Copy, increases the reference count
co::shared<co::string> t = s;
co::println("use_count = ", s.use_count()); // 2
// An empty object has an internal pointer of nullptr; copying does not create sharing
co::shared<int> x;
co::shared<int> y;
y = x;
// *x == 7, y == nullptr
x = co::make_shared<int>(7);
co::println(x.use_count()); // 1
co::println(y.use_count()); // 0
#unique / shared Construction Constraints
co::unique<T> and co::shared<T> are not allowed to be constructed directly from dynamically allocated memory; you can only use:
co::make_unique<T>(args...);
co::make_shared<T>(args...);
#co::stl_allocator
Used to replace std::allocator in STL containers.
co/stl.h provides commonly used STL containers with the memory allocator already replaced by co::stl_allocator.
Example:
std::vector<int, co::stl_allocator<int>> v;
v.push_back(1);
v.push_back(2);
co::vecotr<int> x;
x.push_back(8);
co::println("x: ", x);
#Notes
- The coost allocator cannot replace
malloc, freeoroperator new, operator delete. co::freerequires the memory size, andco::reallocrequires the old size, which differs from standard library semantics.- Memory allocated by
mallocornewcannot be freed withco::free, and vice versa.