react-native-windows/vnext/Mso/memoryApi/memoryApi.h

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