blob: 9f5511b09a077ad20edec3a232d74e24d1e5a6c0 [file]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/fidl/cpp/wire/arena.h>
#include <lib/fidl/cpp/wire/message_storage.h>
#include <unordered_set>
#include <zxtest/zxtest.h>
using ::fidl::internal::AnyBufferAllocator;
TEST(AnyBufferAllocator, WrapBufferSpan) {
constexpr uint32_t kFullAllocSize = 128;
constexpr uint32_t kHalfAllocSize = 64;
uint8_t bytes[kFullAllocSize];
fidl::BufferSpan buffer_span(bytes, sizeof(bytes));
AnyBufferAllocator allocator = ::fidl::internal::MakeAnyBufferAllocator(buffer_span);
uint8_t* addr;
addr = allocator.Allocate(kHalfAllocSize);
EXPECT_EQ(bytes, addr);
addr = allocator.Allocate(kHalfAllocSize);
EXPECT_EQ(&bytes[kHalfAllocSize], addr);
// After allocating the half size twice, the buffer should be exhausted now.
addr = allocator.Allocate(kHalfAllocSize);
EXPECT_NULL(addr);
addr = allocator.Allocate(1);
EXPECT_NULL(addr);
}
TEST(AnyBufferAllocator, WrapBufferSpanOverflow) {
constexpr uint32_t kBufferSize = 128;
uint8_t bytes[kBufferSize];
fidl::BufferSpan buffer_span(bytes, sizeof(bytes));
AnyBufferAllocator allocator = ::fidl::internal::MakeAnyBufferAllocator(buffer_span);
uint8_t* addr;
addr = allocator.Allocate(1);
EXPECT_NOT_NULL(addr);
addr = allocator.Allocate(std::numeric_limits<uint32_t>::max());
EXPECT_NULL(addr);
addr = allocator.Allocate(1);
EXPECT_NOT_NULL(addr);
}
TEST(AnyBufferAllocator, WrapArena) {
constexpr uint32_t kFullAllocSize = 128;
constexpr size_t kNumAllocations = 100;
fidl::Arena arena;
AnyBufferAllocator allocator = ::fidl::internal::MakeAnyBufferAllocator(arena);
// Invariants:
// - None of the returned addresses should reappear (no reusing of previously
// allocated parts).
// - Writing to the allocated buffer should not fail.
std::unordered_set<uint8_t*> addresses;
for (size_t i = 0; i < kNumAllocations; i++) {
uint8_t* addr = allocator.Allocate(kFullAllocSize);
EXPECT_NOT_NULL(addr);
memset(addr, 0, kFullAllocSize);
EXPECT_TRUE(addresses.find(addr) == addresses.end());
addresses.insert(addr);
}
}
// Test that the user could extend `.buffer(...)` calls with their custom memory
// resource by defining a |MakeFidlAnyMemoryResource| function.
namespace my_fancy_memory_resource {
// A simple allocator that delegates to `new` and `delete`.
struct HeapAllocator {
~HeapAllocator() {
for (const auto& allocation : allocations_) {
delete[] allocation;
}
}
private:
friend fidl::AnyMemoryResource MakeFidlAnyMemoryResource(HeapAllocator& a) {
class HeapAllocatorMemoryResource : public fidl::MemoryResource {
public:
explicit HeapAllocatorMemoryResource(HeapAllocator& a) : a_(&a) {}
uint8_t* Allocate(uint32_t num_bytes) final {
uint8_t* allocation = new uint8_t[num_bytes];
a_->allocations_.push_back(allocation);
return allocation;
}
private:
HeapAllocator* a_;
};
return fidl::AnyMemoryResource(std::in_place_type_t<HeapAllocatorMemoryResource>{}, a);
}
std::vector<uint8_t*> allocations_;
};
} // namespace my_fancy_memory_resource
TEST(AnyBufferAllocator, WrapCustomMemoryResource) {
my_fancy_memory_resource::HeapAllocator custom_allocator{};
AnyBufferAllocator allocator = ::fidl::internal::MakeAnyBufferAllocator(custom_allocator);
constexpr uint32_t kAllocSize = 10;
uint8_t* bytes = allocator.Allocate(kAllocSize);
ASSERT_NOT_NULL(bytes);
memset(bytes, 0xFF, kAllocSize);
for (size_t i = 0; i < kAllocSize; i++) {
EXPECT_EQ(0xFF, bytes[i]);
}
}