IVGCVSW-6301 Create the MemoryManager class

Signed-off-by: Teresa Charlin <teresa.charlinreyes@arm.com>
Signed-off-by: Finn Williams <finn.williams@arm.com>
Change-Id: Ia41b5252d695daabd5afaf1b2267444d24be173a
diff --git a/src/backends/backendsCommon/CMakeLists.txt b/src/backends/backendsCommon/CMakeLists.txt
index a18ee33..90fb376 100644
--- a/src/backends/backendsCommon/CMakeLists.txt
+++ b/src/backends/backendsCommon/CMakeLists.txt
@@ -1,11 +1,9 @@
 #
-# Copyright © 2017 Arm Ltd. All rights reserved.
+# Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
 # SPDX-License-Identifier: MIT
 #
 
 list(APPEND armnnBackendsCommon_sources
-    TensorHandle.cpp
-    TensorHandle.hpp
     DynamicBackend.cpp
     DynamicBackend.hpp
     DynamicBackendUtils.cpp
@@ -29,10 +27,14 @@
     MemoryOptimizerStrategyFactory.hpp
     MemoryOptimizerStrategyLibrary.cpp
     MemoryOptimizerStrategyLibrary.hpp
+    MemoryManager.cpp
+    MemoryManager.hpp
     MemSyncWorkload.cpp
     MemSyncWorkload.hpp
     OptimizationViews.cpp
     OptimizationViews.hpp
+    TensorHandle.cpp
+    TensorHandle.hpp
     TensorHandleFactoryRegistry.cpp
     TensorHandleFactoryRegistry.hpp
     UnmapWorkload.cpp
diff --git a/src/backends/backendsCommon/MemoryManager.cpp b/src/backends/backendsCommon/MemoryManager.cpp
new file mode 100644
index 0000000..1c109c3
--- /dev/null
+++ b/src/backends/backendsCommon/MemoryManager.cpp
@@ -0,0 +1,53 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "MemoryManager.hpp"
+
+#include <armnn/utility/IgnoreUnused.hpp>
+
+namespace armnn
+{
+
+void MemoryManager::StoreMemToAllocate(std::vector<BufferStorage> bufferStorageVector,
+                                       ICustomAllocator* customAllocator,
+                                       const size_t typeAlignment)
+{
+    IgnoreUnused(typeAlignment);
+    m_AllocatorBufferStoragePairVector.emplace_back(std::make_pair<Allocator, std::vector<BufferStorage>>(
+                                                    Allocator{customAllocator},
+                                                    std::move(bufferStorageVector)));
+}
+
+void MemoryManager::Allocate()
+{
+    for (auto& m_AllocatorBufferStoragePair : m_AllocatorBufferStoragePairVector)
+    {
+        auto& allocator = m_AllocatorBufferStoragePair.first;
+        for (auto&& bufferStorage : m_AllocatorBufferStoragePair.second)
+        {
+           bufferStorage.m_Buffer = allocator.m_CustomAllocator->allocate(bufferStorage.m_BufferSize, 0);
+
+            for (auto tensorMemory : bufferStorage.m_TensorMemoryVector)
+            {
+                tensorMemory->m_Data = allocator.m_CustomAllocator->GetMemoryRegionAtOffset(bufferStorage.m_Buffer,
+                                                                                            tensorMemory->m_Offset);
+            }
+        }
+    }
+}
+
+void MemoryManager::Deallocate()
+{
+    for (auto& m_AllocatorBufferStoragePair : m_AllocatorBufferStoragePairVector)
+    {
+        auto& allocator = m_AllocatorBufferStoragePair.first;
+        for (auto&& bufferStorage : m_AllocatorBufferStoragePair.second)
+        {
+            allocator.m_CustomAllocator->free(bufferStorage.m_Buffer);
+        }
+    }
+}
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/MemoryManager.hpp b/src/backends/backendsCommon/MemoryManager.hpp
new file mode 100644
index 0000000..cbd6fcf
--- /dev/null
+++ b/src/backends/backendsCommon/MemoryManager.hpp
@@ -0,0 +1,60 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <armnn/backends/ICustomAllocator.hpp>
+
+namespace armnn
+{
+struct Allocator
+{
+    /// Pointer to @ICustomAllocator.
+    ICustomAllocator* m_CustomAllocator{};
+    /// Value which the size of each buffer (actual data size + padding) has to be a multiple of.
+    size_t m_Alignment = 0 ;
+};
+
+struct TensorMemory
+{
+    /// Number of bytes the value is away from the @BufferStorage.m_Buffer.
+    size_t m_Offset{};
+    /// Pointer to the tensor value.
+    void* m_Data = nullptr;
+    /// Identifier to be used by the @LoadedNetwork to order the tensors.
+    unsigned int m_OutputSlotId{};
+};
+
+struct BufferStorage
+{
+    /// Vector of pointer to @TensorMemory.
+    std::vector<TensorMemory*> m_TensorMemoryVector;
+    /// Total size of the buffer.
+    size_t m_BufferSize;
+    /// Pointer to the first element of the buffer.
+    void* m_Buffer = nullptr;
+};
+
+class MemoryManager
+{
+public:
+    /// Initialization method to store in @m_AllocatorBufferStoragePairVector all information needed.
+    /// @param[in] bufferStorageVector - Vector of @BufferStorage.
+    /// @param[in] customAllocator - Pointer to @ICustomAllocator.
+    /// @param[in] typeAlignment - Optional parameter. Value of which the size of each value has to be multiple of.
+    void StoreMemToAllocate(std::vector<BufferStorage> bufferStorageVector,
+                            ICustomAllocator* customAllocator,
+                            size_t typeAlignment = 0);
+
+    /// Allocate the amount of memory indicated by @m_BufferSize, and
+    /// point each @m_Data to each correspondent Tensor so that they are @m_Offset bytes separated.
+    void Allocate();
+
+    /// Deallocate memory
+    void Deallocate();
+
+private:
+    std::vector<std::pair<Allocator, std::vector<BufferStorage>>> m_AllocatorBufferStoragePairVector;
+};
+
+} // namespace armnn
diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt
index b90407f..9272ae7 100644
--- a/src/backends/backendsCommon/test/CMakeLists.txt
+++ b/src/backends/backendsCommon/test/CMakeLists.txt
@@ -13,6 +13,7 @@
     ChannelShuffleEndToEndTestImpl.hpp
     ComparisonEndToEndTestImpl.hpp
     CompatibilityTests.cpp
+    ConcatEndToEndTestImpl.hpp
     Convolution3dEndToEndTestImpl.hpp
     CustomMemoryOptimizerStrategyTests.cpp
     DefaultAsyncExecuteTest.cpp
@@ -35,7 +36,7 @@
     LayerTests.hpp
     LogSoftmaxEndToEndTestImpl.cpp
     LogSoftmaxEndToEndTestImpl.hpp
-    ConcatEndToEndTestImpl.hpp
+    MemoryManagerTests.cpp
     MockBackend.cpp
     MockBackend.hpp
     MockBackendId.hpp
diff --git a/src/backends/backendsCommon/test/MemoryManagerTests.cpp b/src/backends/backendsCommon/test/MemoryManagerTests.cpp
new file mode 100644
index 0000000..b5f2db4
--- /dev/null
+++ b/src/backends/backendsCommon/test/MemoryManagerTests.cpp
@@ -0,0 +1,107 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <backendsCommon/MemoryManager.hpp>
+#include <armnn/utility/IgnoreUnused.hpp>
+
+#include <doctest/doctest.h>
+#include <numeric>
+
+namespace armnn
+{
+/// @brief Class that implements a sample custom allocator.
+class SampleCustomAllocator : public armnn::ICustomAllocator
+{
+public:
+    SampleCustomAllocator() = default;
+
+    void* allocate(size_t size, size_t alignment) override
+    {
+        IgnoreUnused(alignment);
+        CHECK(size == m_Values.size());
+        m_CounterAllocate+=1;
+        return m_Values.data();
+    }
+
+    void free(void* ptr) override
+    {
+        CHECK(ptr == m_Values.data());
+        m_CounterFree+=1;
+    }
+
+    armnn::MemorySource GetMemorySourceType() override
+    {
+        return armnn::MemorySource::Malloc;
+    }
+
+    virtual void* GetMemoryRegionAtOffset(void* buffer, size_t offset, size_t alignment = 0 ) override
+    {
+        IgnoreUnused(alignment);
+        return (static_cast<char*>(buffer) + offset);
+    }
+
+    /// Holds the data in the tensors. Create for testing purposes.
+    std::vector<uint8_t> m_Values;
+    /// Counts the number of times the function allocate is called.
+    unsigned long m_CounterAllocate= 0;
+    /// Counts the number of times the function free is called.
+    unsigned long m_CounterFree = 0;
+};
+
+TEST_SUITE("MemoryManagerTests")
+{
+/// Unit test Storing, Allocating and Deallocating with a custom allocator.
+TEST_CASE("MemoryManagerTest")
+{
+    using namespace armnn;
+
+    // Create mock up bufferStorageVector with 2 BufferStorage with the same TensorMemory
+    size_t numTensors = 5;
+    std::vector<TensorMemory*> tensorMemoryPointerVector(numTensors);
+    std::vector<TensorMemory> tensorMemoryVector;
+    tensorMemoryVector.reserve(numTensors);
+
+    std::vector<size_t> offsets(numTensors);
+    std::iota(std::begin(offsets), std::end(offsets), 0);
+
+    for (uint idx = 0; idx < tensorMemoryPointerVector.size(); ++idx)
+    {
+        tensorMemoryVector.emplace_back(TensorMemory{offsets[idx], nullptr, 0});
+        tensorMemoryPointerVector[idx] = &tensorMemoryVector[idx];
+    }
+
+    std::vector<BufferStorage> bufferStorageVector;
+    bufferStorageVector.emplace_back(BufferStorage{tensorMemoryPointerVector, numTensors});
+    bufferStorageVector.emplace_back(BufferStorage{tensorMemoryPointerVector, numTensors});
+
+    // Create an instance of the SampleCustomAllocator
+    SampleCustomAllocator customAllocator = SampleCustomAllocator();
+    customAllocator.m_Values = {10, 11, 12, 13, 14};
+    // Check that the test was set up correctly
+    CHECK(customAllocator.m_Values.size() == numTensors);
+
+    // Utilise 3 functions in the MemoryManager. Check the counters and the pointer to the values are correct.
+    MemoryManager memoryManager;
+    memoryManager.StoreMemToAllocate(bufferStorageVector, &customAllocator);
+
+    memoryManager.Allocate();
+    CHECK(customAllocator.m_CounterAllocate == bufferStorageVector.size());
+    for (const auto& bufferStorage : bufferStorageVector)
+    {
+        uint idx = 0;
+        for (auto tensorMemory : bufferStorage.m_TensorMemoryVector)
+        {
+            auto value = reinterpret_cast<uint8_t *>(tensorMemory->m_Data);
+            CHECK(customAllocator.m_Values[idx] == *value);
+            idx += 1;
+        }
+    }
+
+    memoryManager.Deallocate();
+    CHECK(customAllocator.m_CounterFree == bufferStorageVector.size());
+}
+}
+
+} // namespace armnn
\ No newline at end of file