|
|
@@ -37,13 +37,14 @@ public:
|
|
|
// A Buffer cannot have a name, because each String contains a buffer
|
|
|
const int64_t size; // The actually used data
|
|
|
const int64_t bufferSize; // The accessible data
|
|
|
+ const int alignment;
|
|
|
uint8_t *data;
|
|
|
std::function<void(uint8_t *)> destructor;
|
|
|
public:
|
|
|
// Create head without data.
|
|
|
BufferImpl();
|
|
|
// Create head with newly allocated data.
|
|
|
- explicit BufferImpl(int64_t newSize);
|
|
|
+ explicit BufferImpl(int64_t newSize, int alignment);
|
|
|
// Create head with inherited data.
|
|
|
BufferImpl(int64_t newSize, uint8_t *newData);
|
|
|
~BufferImpl();
|
|
|
@@ -55,23 +56,31 @@ public:
|
|
|
|
|
|
// Internal methods
|
|
|
|
|
|
-// Buffers are aligned and padded for the deafult SIMD vector size, so that vectorization can be efficient.
|
|
|
-static const int buffer_alignment = DSR_DEFAULT_ALIGNMENT;
|
|
|
+// Get the largest alignment and confirm that it is a power of two.
|
|
|
+static int getFinalAlignment(int requestedAlignment) {
|
|
|
+ // Find any power of two alignment divisible by both requestedAlignment and DSR_DEFAULT_ALIGNMENT
|
|
|
+ int largestAlignment = max(requestedAlignment, DSR_DEFAULT_ALIGNMENT);
|
|
|
+ for (uint32_t e = 0; e < 32; e++) {
|
|
|
+ uint32_t requestedAlignment = 1 << e;
|
|
|
+ if (1 << e == largestAlignment) return largestAlignment;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
|
|
|
// If this C++ version additionally includes the C11 features then we may assume that aligned_alloc is available
|
|
|
#ifdef _ISOC11_SOURCE
|
|
|
// Allocate data of newSize and write the corresponding destructor function to targetDestructor
|
|
|
- static uint8_t* buffer_allocate(int64_t newSize, std::function<void(uint8_t *)>& targetDestructor) {
|
|
|
- uint8_t* allocation = (uint8_t*)aligned_alloc(buffer_alignment, newSize);
|
|
|
+ static uint8_t* buffer_allocate(int64_t newSize, int alignment, std::function<void(uint8_t *)>& targetDestructor) {
|
|
|
+ uint8_t* allocation = (uint8_t*)aligned_alloc(alignment, newSize);
|
|
|
targetDestructor = [](uint8_t *data) { free(data); };
|
|
|
return allocation;
|
|
|
}
|
|
|
#else
|
|
|
- static const uintptr_t buffer_alignment_mask = ~((uintptr_t)(buffer_alignment - 1));
|
|
|
// Allocate data of newSize and write the corresponding destructor function to targetDestructor
|
|
|
- static uint8_t* buffer_allocate(int64_t newSize, std::function<void(uint8_t *)>& targetDestructor) {
|
|
|
- uintptr_t padding = buffer_alignment - 1;
|
|
|
+ static uint8_t* buffer_allocate(int64_t newSize, int alignment, std::function<void(uint8_t *)>& targetDestructor) {
|
|
|
+ uintptr_t padding = alignment - 1;
|
|
|
uint8_t* allocation = (uint8_t*)malloc(newSize + padding);
|
|
|
+ uintptr_t buffer_alignment_mask = ~((uintptr_t)(alignment - 1));
|
|
|
uint8_t* aligned = (uint8_t*)(((uintptr_t)allocation + padding) & buffer_alignment_mask);
|
|
|
uintptr_t offset = allocation - aligned;
|
|
|
targetDestructor = [offset](uint8_t *data) { free(data - offset); };
|
|
|
@@ -79,12 +88,13 @@ static const int buffer_alignment = DSR_DEFAULT_ALIGNMENT;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-BufferImpl::BufferImpl() : size(0), bufferSize(0), data(nullptr) {}
|
|
|
+BufferImpl::BufferImpl() : size(0), bufferSize(0), alignment(DSR_DEFAULT_ALIGNMENT), data(nullptr) {}
|
|
|
|
|
|
-BufferImpl::BufferImpl(int64_t newSize) :
|
|
|
+BufferImpl::BufferImpl(int64_t newSize, int alignment) :
|
|
|
size(newSize),
|
|
|
- bufferSize(roundUp(newSize, buffer_alignment)) {
|
|
|
- this->data = buffer_allocate(this->bufferSize, this->destructor);
|
|
|
+ bufferSize(roundUp(newSize, alignment)),
|
|
|
+ alignment(alignment) {
|
|
|
+ this->data = buffer_allocate(this->bufferSize, alignment, this->destructor);
|
|
|
if (this->data == nullptr) {
|
|
|
throwError(U"Failed to allocate buffer of ", newSize, " bytes!\n");
|
|
|
}
|
|
|
@@ -92,7 +102,7 @@ BufferImpl::BufferImpl(int64_t newSize) :
|
|
|
}
|
|
|
|
|
|
BufferImpl::BufferImpl(int64_t newSize, uint8_t *newData)
|
|
|
-: size(newSize), bufferSize(newSize), data(newData), destructor([](uint8_t *data) { free(data); }) {}
|
|
|
+: size(newSize), bufferSize(newSize), alignment(1), data(newData), destructor([](uint8_t *data) { free(data); }) {}
|
|
|
|
|
|
BufferImpl::~BufferImpl() {
|
|
|
if (this->data) {
|
|
|
@@ -111,22 +121,28 @@ Buffer buffer_clone(const Buffer &buffer) {
|
|
|
// No need to clone when there is no shared data.
|
|
|
return buffer;
|
|
|
} else {
|
|
|
- // Clone the data so that the allocations can be modified individually.
|
|
|
- Buffer newBuffer = std::make_shared<BufferImpl>(buffer->size);
|
|
|
+ // Clone the data so that content of the allocations can be modified individually without affecting each other.
|
|
|
+ Buffer newBuffer = std::make_shared<BufferImpl>(buffer->size, max(buffer->alignment, DSR_DEFAULT_ALIGNMENT));
|
|
|
memcpy(newBuffer->data, buffer->data, buffer->size);
|
|
|
return newBuffer;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-Buffer buffer_create(int64_t newSize) {
|
|
|
+Buffer buffer_create(int64_t newSize, int minimumAlignment) {
|
|
|
if (newSize < 0) newSize = 0;
|
|
|
if (newSize == 0) {
|
|
|
// Allocate empty head to indicate that an empty buffer exists.
|
|
|
return std::make_shared<BufferImpl>();
|
|
|
} else {
|
|
|
// Allocate head and data.
|
|
|
- return std::make_shared<BufferImpl>(newSize);
|
|
|
+ int finalAlignment = getFinalAlignment(minimumAlignment);
|
|
|
+ if (finalAlignment != -1) {
|
|
|
+ return std::make_shared<BufferImpl>(newSize, finalAlignment);
|
|
|
+ } else {
|
|
|
+ throwError(U"buffer_create: Minimum alignment ", minimumAlignment, " is not a power of two!\n");
|
|
|
+ return Buffer(); // Invalid alignment argument was not a power of two.
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|