| /* |
| * Copyright 2017 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "nb/std_allocator.h" |
| |
| #include <map> |
| #include <vector> |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace nb { |
| namespace { |
| |
| struct CountingAllocator { |
| static int num_allocs; |
| static int64_t num_bytes; |
| static void Reset() { |
| num_allocs = 0; |
| num_bytes = 0; |
| } |
| |
| static void* Allocate(size_t n) { |
| ++num_allocs; |
| num_bytes += n; |
| return SbMemoryAllocate(n); |
| } |
| |
| static void Deallocate(void* ptr, size_t n) { |
| num_bytes -= n; |
| --num_allocs; |
| SbMemoryDeallocate(ptr); |
| } |
| }; |
| int CountingAllocator::num_allocs = 0; |
| int64_t CountingAllocator::num_bytes = 0; |
| |
| // Test the expectation that vector will go through the supplied allocator. |
| TEST(StdAllocator, vector) { |
| CountingAllocator::Reset(); |
| |
| typedef std::vector<int, StdAllocator<int, CountingAllocator> > IntVector; |
| |
| EXPECT_EQ(0, CountingAllocator::num_allocs); |
| EXPECT_EQ(0, CountingAllocator::num_bytes); |
| |
| IntVector* int_vector = new IntVector; |
| int_vector->push_back(0); |
| |
| for (int i = 0; i < 10; ++i) { |
| int_vector->push_back(i); |
| } |
| |
| // We aren't sure how much allocation is here, but we know it's more than 0. |
| EXPECT_LT(0, CountingAllocator::num_allocs); |
| EXPECT_LT(0, CountingAllocator::num_bytes); |
| |
| delete int_vector; |
| |
| EXPECT_EQ(0, CountingAllocator::num_allocs); |
| EXPECT_EQ(0, CountingAllocator::num_bytes); |
| |
| CountingAllocator::Reset(); |
| } |
| |
| // Test the expectation that map will go through the supplied allocator. |
| TEST(StdAllocator, map) { |
| CountingAllocator::Reset(); |
| |
| typedef typename std::map<int, int>::value_type value_type; |
| |
| typedef std::map<int, int, std::less<int>, |
| StdAllocator<value_type, CountingAllocator> > IntMap; |
| |
| EXPECT_EQ(0, CountingAllocator::num_allocs); |
| EXPECT_EQ(0, CountingAllocator::num_bytes); |
| |
| IntMap* int_map = new IntMap; |
| for (int i = 0; i < 10; ++i) { |
| (*int_map)[i] = i; |
| } |
| |
| // We aren't sure how much allocation is here, but we know it's more than 0. |
| EXPECT_LT(0, CountingAllocator::num_allocs); |
| EXPECT_LT(0, CountingAllocator::num_bytes); |
| |
| delete int_map; |
| |
| EXPECT_EQ(0, CountingAllocator::num_allocs); |
| EXPECT_EQ(0, CountingAllocator::num_bytes); |
| |
| CountingAllocator::Reset(); |
| } |
| |
| class CountingDynamicAllocator : public nb::Allocator { |
| public: |
| CountingDynamicAllocator() : bytes_allocated_(0), num_allocations_(0) {} |
| void* Allocate(size_t size) override { |
| bytes_allocated_ += size; |
| ++num_allocations_; |
| return SbMemoryAllocate(size); |
| } |
| |
| void* Allocate(std::size_t size, std::size_t alignment) override { |
| EXPECT_TRUE(false) << "Unexpected that aligned version is called."; |
| return Allocate(size); |
| } |
| |
| void Free(void* memory) override { |
| FreeWithSize(memory, 0); |
| ASSERT_TRUE(false) << "Unexpected that free without size " |
| "version is called."; |
| } |
| |
| void FreeWithSize(void* ptr, size_t optional_size) override { |
| SbMemoryDeallocate(ptr); |
| |
| bytes_allocated_ -= optional_size; |
| --num_allocations_; |
| } |
| |
| std::size_t GetCapacity() const override { |
| EXPECT_TRUE(false) << "Unexpected that GetCapacity()."; |
| return 0; |
| } |
| |
| std::size_t GetAllocated() const override { |
| return static_cast<size_t>(bytes_allocated_); |
| } |
| |
| void PrintAllocations() const override {} |
| |
| int64_t bytes_allocated_; |
| int64_t num_allocations_; |
| }; |
| |
| // Test the expectation that vector will go through the supplied allocator. |
| TEST(StdDynamicAllocator, vector) { |
| typedef StdDynamicAllocator<int> IntAllocator; |
| typedef std::vector<int, IntAllocator> IntVector; |
| |
| CountingDynamicAllocator counting_allocator; |
| |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| |
| IntVector* int_vector = new IntVector(IntAllocator(&counting_allocator)); |
| int_vector->push_back(0); |
| |
| for (int i = 0; i < 10; ++i) { |
| int_vector->push_back(i); |
| } |
| |
| // We aren't sure how much allocation is here, but we know it's more than 0. |
| EXPECT_LT(0, counting_allocator.bytes_allocated_); |
| EXPECT_LT(0, counting_allocator.num_allocations_); |
| |
| delete int_vector; |
| |
| // Expect that all allocations are destroyed now. |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| } |
| |
| // Test the expectation that vector will go through the supplied allocator. |
| TEST(StdDynamicAllocator, map) { |
| typedef typename std::map<int, int>::value_type value_type; |
| typedef StdDynamicAllocator<value_type> IntAllocator; |
| typedef std::map<int, int, std::less<int>, IntAllocator> IntMap; |
| |
| CountingDynamicAllocator counting_allocator; |
| |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| |
| IntMap* int_map = new IntMap(std::less<int>(), // required. |
| IntAllocator(&counting_allocator)); |
| for (int i = 0; i < 10; ++i) { |
| (*int_map)[i] = i; |
| } |
| |
| // We aren't sure how much allocation is here, but we know it's more than 0. |
| EXPECT_LT(0, counting_allocator.bytes_allocated_); |
| EXPECT_LT(0, counting_allocator.num_allocations_); |
| |
| delete int_map; |
| |
| // Expect that all allocations are destroyed now. |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| } |
| |
| // Test the expectation that a copied vector will go through the allocator from |
| // the first vector. |
| TEST(StdDynamicAllocator, vector_copy) { |
| typedef StdDynamicAllocator<int> IntAllocator; |
| typedef std::vector<int, IntAllocator> IntVector; |
| |
| CountingDynamicAllocator counting_allocator; |
| |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| |
| IntVector* int_vector = new IntVector(IntAllocator(&counting_allocator)); |
| int_vector->push_back(0); |
| |
| for (int i = 0; i < 10; ++i) { |
| int_vector->push_back(i); |
| } |
| |
| // We aren't sure how much allocation is here, but we know it's more than 0. |
| EXPECT_LT(0, counting_allocator.bytes_allocated_); |
| EXPECT_LT(0, counting_allocator.num_allocations_); |
| |
| int64_t saved_bytes_allocated = counting_allocator.bytes_allocated_; |
| int64_t saved_num_allocations = counting_allocator.num_allocations_; |
| |
| IntVector* int_vector_copy = new IntVector(*int_vector); |
| for (int i = 0; i < 10; ++i) { |
| int_vector_copy->push_back(i); |
| } |
| |
| EXPECT_LT(saved_bytes_allocated, counting_allocator.bytes_allocated_); |
| EXPECT_LT(saved_num_allocations, counting_allocator.num_allocations_); |
| |
| delete int_vector_copy; |
| delete int_vector; |
| |
| // Expect that all allocations are destroyed now. |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| } |
| |
| // Test the expectation that vector will go through the supplied allocator. |
| TEST(StdDynamicAllocator, map_copy) { |
| typedef typename std::map<int, int>::value_type value_type; |
| typedef StdDynamicAllocator<value_type> IntAllocator; |
| typedef std::map<int, int, std::less<int>, IntAllocator> IntMap; |
| |
| CountingDynamicAllocator counting_allocator; |
| |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| |
| IntMap* int_map = new IntMap(std::less<int>(), // required. |
| IntAllocator(&counting_allocator)); |
| for (int i = 0; i < 10; ++i) { |
| (*int_map)[i] = i; |
| } |
| |
| int64_t saved_bytes_allocated = counting_allocator.bytes_allocated_; |
| int64_t saved_num_allocations = counting_allocator.num_allocations_; |
| |
| IntMap* int_map_copy = new IntMap(*int_map); |
| |
| for (int i = 0; i < 10; ++i) { |
| (*int_map_copy)[i] = i; |
| } |
| |
| EXPECT_LT(saved_bytes_allocated, counting_allocator.bytes_allocated_); |
| EXPECT_LT(saved_num_allocations, counting_allocator.num_allocations_); |
| |
| delete int_map_copy; |
| delete int_map; |
| |
| // Expect that all allocations are destroyed now. |
| EXPECT_EQ(0, counting_allocator.bytes_allocated_); |
| EXPECT_EQ(0, counting_allocator.num_allocations_); |
| } |
| |
| } // namespace |
| } // namespace nb |