426 строки
11 KiB
C++
426 строки
11 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
/**
|
|
This file contains the basic set of allocation APIs that anyone can use:
|
|
- Non-throwing Mso::Memory allocation APIs
|
|
- Allocation flags
|
|
- Mso::MemoryPtr smart pointer for Mso::Memory APIs
|
|
|
|
See MsoMemory.h for information about operator new.
|
|
*/
|
|
#pragma once
|
|
#ifndef MSO_MEMORYAPI_MEMORYAPI_H
|
|
#define MSO_MEMORYAPI_MEMORYAPI_H
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <memory>
|
|
#include "compilerAdapters/cppMacrosDebug.h"
|
|
#include "compilerAdapters/functionDecorations.h"
|
|
#include "crash/verifyElseCrash.h"
|
|
#include "debugAssertApi/debugAssertApi.h"
|
|
#include "memoryApi/memoryLeakScope.h"
|
|
#include "oacr/oacr.h"
|
|
#include "smartPtr/smartPointerBase.h"
|
|
|
|
namespace Mso {
|
|
namespace Memory {
|
|
namespace Details {
|
|
template <typename T>
|
|
struct Emplacer {
|
|
template <typename... Args>
|
|
static void Place(_Inout_updates_bytes_all_(sizeof(T)) void *mem, Args &&...args) noexcept {
|
|
new (mem) T(std::forward<Args>(args)...);
|
|
}
|
|
};
|
|
|
|
template <typename T, size_t N>
|
|
struct Emplacer<T[N]> {
|
|
template <typename... Args>
|
|
static void Place(void *mem, Args &&...args) {
|
|
new (mem) T[N]{std::forward<Args>(args)...};
|
|
}
|
|
};
|
|
|
|
template <typename T, typename Enable = void>
|
|
struct Destructor {
|
|
static void Destruct(T &) noexcept { /* noop */
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct Destructor<T, typename std::enable_if<std::is_destructible<T>::value && !std::is_pod<T>::value>::type> {
|
|
static void Destruct(T &obj) noexcept {
|
|
UNREFERENCED_PARAMETER(obj);
|
|
obj.~T();
|
|
}
|
|
};
|
|
} // namespace Details
|
|
|
|
/**
|
|
Mso::Memory::AllocFlags
|
|
*/
|
|
namespace AllocFlags {
|
|
enum Enum : unsigned int {
|
|
// check this memory during shutdown leak detection
|
|
ShutdownLeak = 0x0001,
|
|
|
|
// ignore this memory during leak detection
|
|
IgnoreLeak = 0x0002,
|
|
|
|
// track this memory using memory marking / idle time leak detection
|
|
MarkingLeak = 0x0004,
|
|
};
|
|
};
|
|
|
|
/**
|
|
Return a new allocation of the requested size (cb)
|
|
Returns nullptr on failure
|
|
*/
|
|
LIBLET_PUBLICAPI_EX("win", "android")
|
|
_Ret_maybenull_ _Post_writable_byte_size_(cb) void *AllocateEx(size_t cb, uint32_t allocFlags) noexcept;
|
|
|
|
/**
|
|
Return a new allocation of the requested size (cb)
|
|
Returns nullptr on failure
|
|
*/
|
|
inline _Ret_maybenull_ _Post_writable_byte_size_(cb) void *Allocate(size_t cb) noexcept {
|
|
return Mso::Memory::AllocateEx(cb, 0);
|
|
}
|
|
|
|
/**
|
|
Reallocate an existing allocation to a new size
|
|
Returns nullptr on failure
|
|
TODO: Do we need ReallocateEx? Only if allocFlags grows.
|
|
*/
|
|
LIBLET_PUBLICAPI_EX("win", "android") _Ret_maybenull_ void *Reallocate(_Inout_ void **ppv, size_t cb) noexcept;
|
|
|
|
template <typename T>
|
|
_Ret_maybenull_ T *Reallocate(T *pT, size_t cb) noexcept {
|
|
return reinterpret_cast<T *>(Reallocate(reinterpret_cast<void **>(&pT), cb));
|
|
}
|
|
|
|
/**
|
|
Return the allocation size of a given pointer
|
|
*/
|
|
LIBLET_PUBLICAPI_EX("win", "android") size_t AllocationSize(_In_opt_ const void *pv) noexcept;
|
|
|
|
/**
|
|
Release a previously allocated block of memory
|
|
*/
|
|
LIBLET_PUBLICAPI_EX("win", "android") void Free(_Pre_maybenull_ _Post_invalid_ void *pv) noexcept;
|
|
|
|
/**
|
|
Disambiguator used to ensure a throwing new
|
|
new (Mso::Memory::throwNew) Zoo();
|
|
*/
|
|
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
|
static const struct throwNew_t {
|
|
throwNew_t() noexcept = default;
|
|
} throwNew;
|
|
|
|
/**
|
|
Disambiguator used to ensure a crashing new
|
|
new (Mso::Memory::failFast) Zoo();
|
|
*/
|
|
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
|
static const struct failFast_t {
|
|
failFast_t() noexcept = default;
|
|
} failFast;
|
|
|
|
/**
|
|
Construct a object of type `T` stored at `mem`.
|
|
|
|
Arguments are forwarded to constructor of `T`.
|
|
*/
|
|
template <typename T, typename... Args>
|
|
static void Place(__inout_bcount(sizeof(T)) void *mem, Args &&...args) {
|
|
Details::Emplacer<T>::Place(mem, std::forward<Args>(args)...);
|
|
}
|
|
|
|
/**
|
|
Manually destruct an object of type `T`.
|
|
*/
|
|
template <typename T, typename = typename std::enable_if<!std::is_array<T>::value>::type>
|
|
static void Destruct(T &obj) noexcept {
|
|
Details::Destructor<T>::Destruct(obj);
|
|
}
|
|
|
|
/**
|
|
Manually destruct a `T[N]`.
|
|
|
|
Destructs contents of the array in reverse order.
|
|
*/
|
|
template <typename T, size_t N>
|
|
static void Destruct(T (&obj)[N]) noexcept {
|
|
size_t i = N;
|
|
while (i--)
|
|
Destruct(obj[i]);
|
|
}
|
|
} // namespace Memory
|
|
} // namespace Mso
|
|
|
|
namespace Mso {
|
|
namespace Memory {
|
|
namespace FailFast {
|
|
/**
|
|
Return a new allocation of the requested size (cb)
|
|
Never returns nullptr
|
|
*/
|
|
_Ret_maybenull_ _Post_writable_byte_size_(cb) inline void *AllocateEx(size_t cb, uint32_t allocFlags) noexcept {
|
|
auto pv = Mso::Memory::AllocateEx(cb, allocFlags);
|
|
if (pv == nullptr)
|
|
CrashWithRecoveryOnOOM();
|
|
return pv;
|
|
}
|
|
|
|
_Ret_maybenull_ _Post_writable_byte_size_(cb) inline void *Allocate(size_t cb) noexcept {
|
|
return AllocateEx(cb, 0);
|
|
}
|
|
} // namespace FailFast
|
|
} // namespace Memory
|
|
} // namespace Mso
|
|
|
|
namespace Mso {
|
|
namespace Memory {
|
|
namespace NoThrow {
|
|
|
|
/**
|
|
non-throwing operator new using memory-marking leak detection
|
|
new (Mso::Memory::NoThrow::MarkingLeak) Zoo();
|
|
*/
|
|
OACR_WARNING_SUPPRESS(SPECIFY_SELECTANY, "Not needed for marker type")
|
|
static const struct MarkingLeak_t {
|
|
MarkingLeak_t() noexcept = default;
|
|
} MarkingLeak;
|
|
|
|
} // namespace NoThrow
|
|
} // namespace Memory
|
|
} // namespace Mso
|
|
|
|
namespace Mso {
|
|
namespace Memory {
|
|
namespace Throw {
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4100)
|
|
/**
|
|
Mso::Memory::Throw::New<T>(args)
|
|
|
|
Allocates object T by passing args to its constructor.
|
|
*/
|
|
template <typename T, typename... TArgs>
|
|
OACR_WARNING_SUPPRESS(NULL_ON_NON_POINTER, "false positive")
|
|
_Ret_notnull_ T *New(TArgs &&...t) {
|
|
Debug(Mso::Memory::AutoShutdownLeakScope scope);
|
|
T *pT = new (std::nothrow) T(std::forward<TArgs>(t)...);
|
|
if (pT == nullptr)
|
|
throw std::bad_alloc();
|
|
return pT;
|
|
}
|
|
#pragma warning(pop)
|
|
} // namespace Throw
|
|
} // namespace Memory
|
|
} // namespace Mso
|
|
|
|
namespace Mso {
|
|
/**
|
|
Frees memory allocated by Mso::Memory
|
|
*/
|
|
template <typename T>
|
|
struct MemoryPtrHelper {
|
|
static void Free(T *pT) noexcept {
|
|
Mso::Memory::Free(pT);
|
|
}
|
|
};
|
|
|
|
/**
|
|
Mso::MemoryPtr
|
|
|
|
Smart pointer for memory allocated by Mso::Memory.
|
|
Suitable for raw memory or vanilla structs.
|
|
Does not run copy constructors or destructors.
|
|
|
|
The equals operator is purposely left out. Use the c'tor or Attach().
|
|
|
|
Mso::MemoryPtr<BYTE> pFoo; // equivalent to BYTE* pFoo;
|
|
*/
|
|
template <typename T, uint32_t allocFlags = 0>
|
|
class MemoryPtr : public Mso::THolder<T *, MemoryPtrHelper<T>> {
|
|
typedef Mso::THolder<T *, MemoryPtrHelper<T>> Super;
|
|
|
|
public:
|
|
MemoryPtr() noexcept {}
|
|
explicit MemoryPtr(_In_opt_ T *pT) noexcept {
|
|
this->Attach(pT);
|
|
} // Takes ownership
|
|
IMPLEMENT_THOLDER_RVALUE_REFS_(MemoryPtr, Super);
|
|
|
|
/**
|
|
AllocBytes
|
|
|
|
Allocate space for the given number of bytes.
|
|
On success, any previously held data is replaced.
|
|
Returns false on OOM / overflow.
|
|
|
|
AllocElem-variants are strongly preferred to avoid integer overflows.
|
|
|
|
Mso::MemoryPtr<BYTE> pbFoo;
|
|
if (pbFoo.AllocBytes(cbData)) { ... }
|
|
*/
|
|
bool AllocBytes(size_t cb) noexcept {
|
|
T *pT = static_cast<T *>(Mso::Memory::AllocateEx(cb, allocFlags));
|
|
if (pT != nullptr)
|
|
this->Attach(pT);
|
|
return (pT != nullptr);
|
|
}
|
|
|
|
/**
|
|
AllocElem
|
|
|
|
Allocate space for cElements + cExtra instances.
|
|
On success, any previously held data is replaced.
|
|
Optional out parameters to receive the number of elements/bytes allocated
|
|
Returns false on OOM / overflow.
|
|
|
|
Mso::MemoryPtr<wchar_t> wzFoo;
|
|
size_t cchFoo = 0;
|
|
if (wzFoo.AllocElemCb(cch, 1, &cchFoo)) { ... }
|
|
*/
|
|
bool AllocElem(
|
|
size_t cElements,
|
|
size_t cExtra = 0,
|
|
_Out_opt_ size_t *pcElements = nullptr,
|
|
_Out_opt_ size_t *pcbAlloc = nullptr) noexcept {
|
|
// TODO: size_t cbAlloc = (msl::utilities::SafeInt<size_t>(cElements) +cExtra) * sizeof(T);
|
|
size_t cbAlloc = (cElements + cExtra) * sizeof(T);
|
|
bool fRet = AllocBytes(cbAlloc);
|
|
if (pcElements != nullptr)
|
|
*pcElements = (fRet ? cbAlloc / sizeof(T) : 0);
|
|
if (pcbAlloc != nullptr)
|
|
*pcbAlloc = (fRet ? cbAlloc : 0);
|
|
return fRet;
|
|
}
|
|
|
|
/**
|
|
AllocOne
|
|
|
|
Allocate space for 1 instance.
|
|
On success, any previously held data is replaced.
|
|
Returns false on OOM / overflow.
|
|
|
|
Mso::MemoryPtr<Foo> pFoo;
|
|
if (pFoo.AllocOne()) { ... }
|
|
*/
|
|
bool AllocOne() noexcept {
|
|
return AllocElem(1);
|
|
}
|
|
|
|
// TODO: Does it make sense for CallocElemCb? See if there is need.
|
|
|
|
/**
|
|
ReallocBytes
|
|
|
|
Reallocate the exact number of bytes.
|
|
Returns false on OOM / overflow.
|
|
|
|
ReallocElem is strongly preferred.
|
|
*/
|
|
bool ReallocBytes(size_t cb) noexcept {
|
|
const T *pT = static_cast<const T *>(Mso::Memory::Reallocate(reinterpret_cast<void **>(&this->m_pT), cb));
|
|
return (pT != nullptr);
|
|
}
|
|
|
|
/**
|
|
ReallocElem
|
|
|
|
Reallocate space for cElements + cExtra instances.
|
|
Optional out parameters to receive the number of elements/bytes allocated
|
|
Returns false on OOM / overflow.
|
|
*/
|
|
bool ReallocElem(
|
|
size_t cElements,
|
|
size_t cExtra = 0,
|
|
_Out_opt_ size_t *pcElements = nullptr,
|
|
_Out_opt_ size_t *pcbAlloc = nullptr) noexcept {
|
|
// TODO: size_t cbAlloc = (msl::utilities::SafeInt<size_t>(cElements) +cExtra) * sizeof(T);
|
|
size_t cbAlloc = (cElements + cExtra) * sizeof(T);
|
|
bool fRet = ReallocBytes(cbAlloc);
|
|
if (pcElements != nullptr)
|
|
*pcElements = (fRet ? cbAlloc / sizeof(T) : 0);
|
|
if (pcbAlloc != nullptr)
|
|
*pcbAlloc = (fRet ? cbAlloc : 0);
|
|
return fRet;
|
|
}
|
|
|
|
/**
|
|
CloneBytes
|
|
|
|
Allocates a new buffer and copies the data into it.
|
|
On success, any previously held data is replaced.
|
|
Returns false on OOM / overflow.
|
|
|
|
CloneElem-variants are strongly preferred to avoid integer overflow.
|
|
FUTURE: I could add cbExtra parameter if it makes sense.
|
|
|
|
Mso::MemoryPtr<BYTE> pbFoo;
|
|
if (pbFoo.CloneBytes(pbSrc, cbSrc)) { ... }
|
|
*/
|
|
bool CloneBytes(_In_opt_bytecount_(cb) const T *pT, size_t cb) noexcept {
|
|
if (pT != nullptr) {
|
|
Mso::MemoryPtr<T, allocFlags> pNew;
|
|
if (pNew.AllocBytes(cb)) {
|
|
memcpy_s(pNew.Get(), cb, pT, cb);
|
|
this->Swap(pNew);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
CloneElem
|
|
|
|
Allocate space and copies cElements instances from the source.
|
|
On success, any previously held data is replaced.
|
|
Returns false on OOM / overflow.
|
|
|
|
Mso::MemoryPtr<FooData> rgFooData;
|
|
if (rgFooData.CloneElem(rgFooDataSrc, cFooDataSrc) { ... }
|
|
*/
|
|
bool CloneElem(_In_opt_count_(cElements) const T *pT, size_t cElements) noexcept {
|
|
// TODO: size_t cbAlloc = msl::utilities::SafeInt<size_t>(cElements) * sizeof(T);
|
|
size_t cbAlloc = (cElements) * sizeof(T);
|
|
return CloneBytes(pT, cbAlloc);
|
|
}
|
|
|
|
/**
|
|
CloneOne
|
|
|
|
Allocates space and copies 1 instance from the source.
|
|
Any previously held data is replaced on success.
|
|
|
|
Mso::MemoryPtr<Foo> pFoo;
|
|
if (pFoo.AllocOne(pFooSrc)) { ... }
|
|
*/
|
|
bool CloneOne(_In_opt_count_(1) const T *pT) noexcept {
|
|
return CloneElem(pT, 1);
|
|
}
|
|
|
|
#ifdef MSO_THOLDER_EXPLICIT_GET_ONLY
|
|
T &operator*() const noexcept {
|
|
return *this->Get();
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
MSO_NO_COPY_CTOR_AND_ASSIGNMENT(MemoryPtr);
|
|
};
|
|
|
|
} // namespace Mso
|
|
|
|
#endif // __cplusplus
|
|
|
|
#endif // MSO_MEMORYAPI_MEMORYAPI_H
|