Repository: duckdb/duckdb
File: src/common/allocator.cpp (C++)

Code before:
      reallocate_function(reallocate_function_p), private_data(std::move(private_data_p)) {
	D_ASSERT(allocate_function);
	D_ASSERT(free_function);
	D_ASSERT(reallocate_function);
#ifdef DEBUG
	if (!private_data) {
		private_data = make_uniq<PrivateAllocatorData>();
	}
	private_data->debug_info = make_uniq<AllocatorDebugInfo>();
#endif
}

Allocator::~Allocator() {
}

data_ptr_t Allocator::AllocateData(idx_t size) {
	D_ASSERT(size > 0);
	if (size >= MAXIMUM_ALLOC_SIZE) {
		D_ASSERT(false);
		throw InternalException("Requested allocation size of %llu is out of range - maximum allocation size is %llu",
		                        size, MAXIMUM_ALLOC_SIZE);
	}
	auto result = allocate_function(private_data.get(), size);
#ifdef DEBUG
	D_ASSERT(private_data);
	if (private_data->free_type != AllocatorFreeType::DOES_NOT_REQUIRE_FREE && private_data->debug_info) {
		private_data->debug_info->AllocateData(result, size);
	}
#endif
	if (!result) {
		throw OutOfMemoryException("Failed to allocate block of %llu bytes (bad allocation)", size);
	}
	return result;
}

void Allocator::FreeData(data_ptr_t pointer, idx_t size) {
	if (!pointer) {
		return;
	}
	D_ASSERT(size > 0);
#ifdef DEBUG
	D_ASSERT(private_data);
	if (private_data->free_type != AllocatorFreeType::DOES_NOT_REQUIRE_FREE && private_data->debug_info) {
		private_data->debug_info->FreeData(pointer, size);
	}
#endif
	free_function(private_data.get(), pointer, size);
}

data_ptr_t Allocator::ReallocateData(data_ptr_t pointer, idx_t old_size, idx_t size) {
	if (!pointer) {

Review comment (bug):
Shouldn't we check `private_data` first? That can also be `nullptr` in non-debug builds. This is in the constructor of `Allocator`:
```C++
#ifdef DEBUG
	if (!private_data) {
		private_data = make_uniq<PrivateAllocatorData>();
	}
	private_data->debug_info = make_uniq<AllocatorDebugInfo>();
#endif
```

We can add a helper method and use that in `Allocate`/`Free`/`Reallocate`:
```C++
private:
    bool ShouldUseDebugInfo() const {
        return private_data && private_data->free_type != AllocatorFreeType::DOES_NOT_REQUIRE_FREE && private_data->debug_info
    }
````
We should also remove the `D_ASSERT(private_data)` as this might not be true for mixed release/debug builds.

Code after:
      reallocate_function(reallocate_function_p), private_data(std::move(private_data_p)) {
	D_ASSERT(allocate_function);
	D_ASSERT(free_function);
	D_ASSERT(reallocate_function);
#ifdef DEBUG
	if (!private_data) {
		private_data = make_uniq<PrivateAllocatorData>();
	}
	private_data->debug_info = make_uniq<AllocatorDebugInfo>();
#endif
}

Allocator::~Allocator() {
}

data_ptr_t Allocator::AllocateData(idx_t size) {
	D_ASSERT(size > 0);
	if (size >= MAXIMUM_ALLOC_SIZE) {
		D_ASSERT(false);
		throw InternalException("Requested allocation size of %llu is out of range - maximum allocation size is %llu",
		                        size, MAXIMUM_ALLOC_SIZE);
	}
	auto result = allocate_function(private_data.get(), size);
#ifdef DEBUG
	if (ShouldUseDebugInfo()) {
		private_data->debug_info->AllocateData(result, size);
	}
#endif
	if (!result) {
		throw OutOfMemoryException("Failed to allocate block of %llu bytes (bad allocation)", size);
	}
	return result;
}

void Allocator::FreeData(data_ptr_t pointer, idx_t size) {
	if (!pointer) {
		return;
	}
	D_ASSERT(size > 0);
#ifdef DEBUG
	if (ShouldUseDebugInfo()) {
		private_data->debug_info->FreeData(pointer, size);
	}
#endif
	free_function(private_data.get(), pointer, size);
}

data_ptr_t Allocator::ReallocateData(data_ptr_t pointer, idx_t old_size, idx_t size) {
	if (!pointer) {
		return nullptr;
	}