diff --git a/CHANGELOG.md b/CHANGELOG.md index 1601a5f5d3205..10b1262057736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Added Castable class: [#1634](https://github.com/dartsim/dart/pull/1634) * Added spdlog support as underlying logging framework: [#1633](https://github.com/dartsim/dart/pull/1633) + * Added MemoryAllocator and CAllocator: [#1636](https://github.com/dartsim/dart/pull/1636) * Dynamics diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b37332e00f16..a7e2968f967d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,7 +93,6 @@ option(DART_ENABLE_SIMD option(DART_BUILD_GUI_OSG "Build osgDart library" ON) option(DART_BUILD_EXTRAS "Build extra projects" OFF) option(DART_CODECOV "Turn on codecov support" OFF) -option(DART_TREAT_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) option(DART_FAST_DEBUG "Add -O1 option for DEBUG mode build" OFF) # GCC and Clang add ANSI-formatted colors when they detect the output medium is a # terminal. However, this doesn't work in some cases such as when the makefile is @@ -170,6 +169,12 @@ else() endif() set_property(CACHE DART_ACTIVE_LOG_LEVEL PROPERTY STRINGS TRACE DEBUG INFO WARN ERROR FATAL OFF) +if(BUILD_TYPE_DEBUG) + option(DART_TREAT_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) +else() + option(DART_TREAT_WARNINGS_AS_ERRORS "Treat warnings as errors" ON) +endif() + #=============================================================================== # Find dependencies #=============================================================================== diff --git a/dart/common/CAllocator.cpp b/dart/common/CAllocator.cpp new file mode 100644 index 0000000000000..8cd19363d78d2 --- /dev/null +++ b/dart/common/CAllocator.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/common/CAllocator.hpp" + +#include "dart/common/Logging.hpp" + +namespace dart::common { + +//============================================================================== +CAllocator::CAllocator() noexcept +{ + // Do nothing +} + +//============================================================================== +CAllocator::~CAllocator() +{ +#ifndef NDEBUG + std::lock_guard lock(m_mutex); + if (!m_map_pointer_to_size.empty()) + { + size_t total_size = 0; + for (auto it : m_map_pointer_to_size) + { + void* pointer = it.first; + size_t size = it.second; + total_size += size; + DART_FATAL("Found memory leak of {} bytes at {}!", size, pointer); + } + DART_FATAL("Found potential memory leak of total {} bytes!", total_size); + } +#endif +} + +//============================================================================== +void* CAllocator::allocate(size_t size) noexcept +{ + if (size == 0) + { + return nullptr; + } + + DART_TRACE("Allocated {} bytes.", size); +#ifndef NDEBUG + std::lock_guard lock(m_mutex); + auto new_ptr = std::malloc(size); + if (new_ptr) + { + m_size += size; + m_peak = std::max(m_peak, m_size); + m_map_pointer_to_size[new_ptr] = size; + } + return new_ptr; +#else + return std::malloc(size); +#endif +} + +//============================================================================== +void CAllocator::deallocate(void* pointer, size_t size) +{ + (void)size; +#ifndef NDEBUG + std::lock_guard lock(m_mutex); + auto it = m_map_pointer_to_size.find(pointer); + if (it != m_map_pointer_to_size.end()) + { + auto allocated_size = it->second; + if (size != allocated_size) + { + DART_FATAL( + "Cannot deallocated memory {} because the deallocating size {} is " + "different from the allocated size {}.", + pointer, + size, + allocated_size); + return; + } + m_size -= size; + m_map_pointer_to_size.erase(it); + DART_TRACE("Deallocated {} bytes.", size); + } + else + { + DART_FATAL( + "Cannot deallocate memory {} that is not allocated by this allocator!", + pointer); + return; + } +#else + DART_TRACE("Deallocated."); +#endif + std::free(pointer); +} + +//============================================================================== +void CAllocator::print(std::ostream& os, int indent) const +{ + if (indent == 0) + { + os << "[CAllocator]\n"; + } + const std::string spaces(indent, ' '); + if (indent != 0) + { + os << spaces << "type: " << getType() << "\n"; + } +#ifndef NDEBUG + std::lock_guard lock(m_mutex); + os << spaces << "size_in_bytes: " << m_size << "\n"; + os << spaces << "peak: " << m_peak << "\n"; +#endif +} + +} // namespace dart::common diff --git a/dart/common/CAllocator.hpp b/dart/common/CAllocator.hpp new file mode 100644 index 0000000000000..adaadb7f5a18f --- /dev/null +++ b/dart/common/CAllocator.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DART_COMMON_CALLOCATOR_HPP_ +#define DART_COMMON_CALLOCATOR_HPP_ + +#ifndef NDEBUG + #include + #include +#endif + +#include "dart/common/MemoryAllocator.hpp" + +namespace dart::common { + +class CAllocator : public MemoryAllocator +{ +public: + /// Constructor + CAllocator() noexcept; + + /// Destructor + ~CAllocator() override; + + DART_STRING_TYPE(CAllocator); + + // Documentation inherited + [[nodiscard]] void* allocate(size_t size) noexcept override; + + // Documentation inherited + void deallocate(void* pointer, size_t size) override; + + // Documentation inherited + void print(std::ostream& os = std::cout, int indent = 0) const override; + +#ifndef NDEBUG +private: + size_t m_size = 0; + size_t m_peak = 0; + std::unordered_map m_map_pointer_to_size; + mutable std::mutex m_mutex; +#endif +}; + +} // namespace dart::common + +#endif // DART_COMMON_CALLOCATOR_HPP_ diff --git a/dart/common/Castable.hpp b/dart/common/Castable.hpp index 3b3d590305477..83c1f11f2683c 100644 --- a/dart/common/Castable.hpp +++ b/dart/common/Castable.hpp @@ -33,6 +33,22 @@ #ifndef DART_COMMON_CASTABLE_HPP_ #define DART_COMMON_CASTABLE_HPP_ +#include + +#define DART_STRING_TYPE(type_name) \ + /** Returns type string. */ \ + [[nodiscard]] static const std::string& getStaticType() \ + { \ + static const std::string type = #type_name; \ + return type; \ + } \ + \ + [[nodiscard]] const std::string& getType() const override \ + { \ + return getStaticType(); \ + } \ + void _ANONYMOUS_FUNCTION_1() + namespace dart::common { /// A CRTP base class that provides an interface for easily casting to the diff --git a/dart/common/MemoryAllocator.cpp b/dart/common/MemoryAllocator.cpp new file mode 100644 index 0000000000000..c721771ec5334 --- /dev/null +++ b/dart/common/MemoryAllocator.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/common/MemoryAllocator.hpp" + +#include "dart/common/CAllocator.hpp" +#include "dart/common/Logging.hpp" + +namespace dart::common { + +//============================================================================== +MemoryAllocator& MemoryAllocator::GetDefault() +{ + static CAllocator default_allocator; + return default_allocator; +} + +//============================================================================== +void MemoryAllocator::print(std::ostream& os, int indent) const +{ + if (indent == 0) + { + os << "[*::print is not implemented]\n"; + } + const std::string spaces(indent, ' '); + os << spaces << "*::print is not implemented:\n"; +} + +//============================================================================== +std::ostream& operator<<(std::ostream& os, const MemoryAllocator& allocator) +{ + allocator.print(os); + return os; +} + +} // namespace dart::common diff --git a/dart/common/MemoryAllocator.hpp b/dart/common/MemoryAllocator.hpp new file mode 100644 index 0000000000000..0a6bec33e1373 --- /dev/null +++ b/dart/common/MemoryAllocator.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DART_COMMON_MEMORYALLOCATOR_HPP_ +#define DART_COMMON_MEMORYALLOCATOR_HPP_ + +#include +#include +#include + +#include "dart/common/Castable.hpp" + +namespace dart::common { + +/// Base class for std::allocator compatible allocators. +class MemoryAllocator : public Castable +{ +public: + /// Returns the default memory allocator + static MemoryAllocator& GetDefault(); + + /// Default constructor + MemoryAllocator() noexcept = default; + + /// Destructor + virtual ~MemoryAllocator() = default; + + /// Returns type string. + [[nodiscard]] virtual const std::string& getType() const = 0; + + /// Allocates @c size bytes of uninitialized storage. + /// + /// @param[in] size: The byte size to allocate sotrage for. + /// @return On success, the pointer to the beginning of newly allocated + /// memory. + /// @return On failure, a null pointer + [[nodiscard]] virtual void* allocate(size_t size) noexcept = 0; + // TODO(JS): Make this constexpr once migrated to C++20 + + template + [[nodiscard]] T* allocateAs(size_t n = 1) noexcept; + + /// Deallocates the storage referenced by the pointer @c p, which must be a + /// pointer obtained by an earlier cal to allocate(). + /// + /// @param[in] pointer: Pointer obtained from allocate(). + virtual void deallocate(void* pointer, size_t size) = 0; + // TODO(JS): Make this constexpr once migrated to C++20 + + /// Allocates uninitialized storage and constructs an object of type T to the + /// allocated storage. + /// + /// @param[in] args...: The constructor arguments to use. + template + [[nodiscard]] T* construct(Args&&... args) noexcept; + + template + [[nodiscard]] T* constructAt(void* pointer, Args&&... args); + + template + [[nodiscard]] T* constructAt(T* pointer, Args&&... args); + + /// Calls the destructor of the object and deallocate the storage. + template + void destroy(T* object) noexcept; + + /// Prints state of the memory allocator + virtual void print(std::ostream& os = std::cout, int indent = 0) const; + + /// Prints state of the memory allocator + friend std::ostream& operator<<( + std::ostream& os, const MemoryAllocator& allocator); +}; + +} // namespace dart::common + +#include "dart/common/detail/MemoryAllocator-impl.hpp" + +#endif // DART_COMMON_MEMORYALLOCATOR_HPP_ diff --git a/dart/common/detail/MemoryAllocator-impl.hpp b/dart/common/detail/MemoryAllocator-impl.hpp new file mode 100644 index 0000000000000..f4650010fd106 --- /dev/null +++ b/dart/common/detail/MemoryAllocator-impl.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DART_COMMON_DETAIL_MEMORYALLOCATOR_HPP_ +#define DART_COMMON_DETAIL_MEMORYALLOCATOR_HPP_ + +#include "dart/common/MemoryAllocator.hpp" + +namespace dart::common { + +//============================================================================== +template +T* MemoryAllocator::allocateAs(size_t n) noexcept +{ + return static_cast(allocate(n * sizeof(T))); +} + +//============================================================================== +template +T* MemoryAllocator::construct(Args&&... args) noexcept +{ + // Allocate new memory for a new object (without calling the constructor) + void* object = allocate(sizeof(T)); + if (!object) + { + return nullptr; + } + + // Call constructor. Return nullptr if failed. + try + { + new (object) T(std::forward(args)...); + } + catch (...) + { + deallocate(object, sizeof(T)); + return nullptr; + } + + return reinterpret_cast(object); +} + +//============================================================================== +template +T* MemoryAllocator::constructAt(void* pointer, Args&&... args) +{ + return ::new (const_cast(static_cast(pointer))) + T(std::forward(args)...); +} + +//============================================================================== +template +T* MemoryAllocator::constructAt(T* pointer, Args&&... args) +{ + return ::new (const_cast(static_cast(pointer))) + T(std::forward(args)...); +} + +//============================================================================== +template +void MemoryAllocator::destroy(T* object) noexcept +{ + if (!object) + { + return; + } + object->~T(); + deallocate(object, sizeof(T)); +} + +} // namespace dart::common + +#endif // DART_COMMON_DETAIL_MEMORYALLOCATOR_HPP_ diff --git a/unittests/unit/CMakeLists.txt b/unittests/unit/CMakeLists.txt index 7e40adfcccd66..8d55a8970307f 100644 --- a/unittests/unit/CMakeLists.txt +++ b/unittests/unit/CMakeLists.txt @@ -7,6 +7,7 @@ foreach(collision_engine target_link_libraries(test_CollisionGroups ${collision_engine}) endif() endforeach() +dart_add_test("unit" test_CAllocator) dart_add_test("unit" test_ContactConstraint) dart_add_test("unit" test_Factory) dart_add_test("unit" test_GenericJoints) diff --git a/unittests/unit/test_CAllocator.cpp b/unittests/unit/test_CAllocator.cpp new file mode 100644 index 0000000000000..12cf1920567f3 --- /dev/null +++ b/unittests/unit/test_CAllocator.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "TestHelpers.hpp" + +using namespace dart; + +//============================================================================== +TEST(CAllocatorTest, Basics) +{ + auto a = common::CAllocator(); + a.print(); + + //--------------------- + // Invalid allocations + //--------------------- + + // size must not be zero + EXPECT_TRUE(a.allocate(0) == nullptr); + + //------------------- + // Valid allocations + //------------------- + + auto mem1 = a.allocate(2); + EXPECT_TRUE(mem1 != nullptr); + + a.deallocate(mem1, 2); + + a.print(); +}