summaryrefslogtreecommitdiffstats
path: root/channels/rdpsnd/client/ios/TPCircularBuffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels/rdpsnd/client/ios/TPCircularBuffer.c')
-rw-r--r--channels/rdpsnd/client/ios/TPCircularBuffer.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/channels/rdpsnd/client/ios/TPCircularBuffer.c b/channels/rdpsnd/client/ios/TPCircularBuffer.c
new file mode 100644
index 0000000..b29f611
--- /dev/null
+++ b/channels/rdpsnd/client/ios/TPCircularBuffer.c
@@ -0,0 +1,153 @@
+//
+// TPCircularBuffer.c
+// Circular/Ring buffer implementation
+//
+// https://github.com/michaeltyson/TPCircularBuffer
+//
+// Created by Michael Tyson on 10/12/2011.
+//
+// Copyright (C) 2012-2013 A Tasty Pixel
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <winpr/wlog.h>
+
+#include "TPCircularBuffer.h"
+#include "rdpsnd_main.h"
+
+#include <mach/mach.h>
+#include <stdio.h>
+
+#define reportResult(result, operation) (_reportResult((result), (operation), __FILE__, __LINE__))
+static inline bool _reportResult(kern_return_t result, const char* operation, const char* file,
+ int line)
+{
+ if (result != ERR_SUCCESS)
+ {
+ WLog_DBG(TAG, "%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
+ return false;
+ }
+ return true;
+}
+
+bool TPCircularBufferInit(TPCircularBuffer* buffer, int length)
+{
+
+ // Keep trying until we get our buffer, needed to handle race conditions
+ int retries = 3;
+ while (true)
+ {
+
+ buffer->length = round_page(length); // We need whole page sizes
+
+ // Temporarily allocate twice the length, so we have the contiguous address space to
+ // support a second instance of the buffer directly after
+ vm_address_t bufferAddress;
+ kern_return_t result = vm_allocate(mach_task_self(), &bufferAddress, buffer->length * 2,
+ VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
+ if (result != ERR_SUCCESS)
+ {
+ if (retries-- == 0)
+ {
+ reportResult(result, "Buffer allocation");
+ return false;
+ }
+ // Try again if we fail
+ continue;
+ }
+
+ // Now replace the second half of the allocation with a virtual copy of the first half.
+ // Deallocate the second half...
+ result = vm_deallocate(mach_task_self(), bufferAddress + buffer->length, buffer->length);
+ if (result != ERR_SUCCESS)
+ {
+ if (retries-- == 0)
+ {
+ reportResult(result, "Buffer deallocation");
+ return false;
+ }
+ // If this fails somehow, deallocate the whole region and try again
+ vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
+ continue;
+ }
+
+ // Re-map the buffer to the address space immediately after the buffer
+ vm_address_t virtualAddress = bufferAddress + buffer->length;
+ vm_prot_t cur_prot, max_prot;
+ result = vm_remap(mach_task_self(),
+ &virtualAddress, // mirror target
+ buffer->length, // size of mirror
+ 0, // auto alignment
+ 0, // force remapping to virtualAddress
+ mach_task_self(), // same task
+ bufferAddress, // mirror source
+ 0, // MAP READ-WRITE, NOT COPY
+ &cur_prot, // unused protection struct
+ &max_prot, // unused protection struct
+ VM_INHERIT_DEFAULT);
+ if (result != ERR_SUCCESS)
+ {
+ if (retries-- == 0)
+ {
+ reportResult(result, "Remap buffer memory");
+ return false;
+ }
+ // If this remap failed, we hit a race condition, so deallocate and try again
+ vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
+ continue;
+ }
+
+ if (virtualAddress != bufferAddress + buffer->length)
+ {
+ // If the memory is not contiguous, clean up both allocated buffers and try again
+ if (retries-- == 0)
+ {
+ WLog_DBG(TAG, "Couldn't map buffer memory to end of buffer");
+ return false;
+ }
+
+ vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
+ vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
+ continue;
+ }
+
+ buffer->buffer = (void*)bufferAddress;
+ buffer->fillCount = 0;
+ buffer->head = buffer->tail = 0;
+
+ return true;
+ }
+ return false;
+}
+
+void TPCircularBufferCleanup(TPCircularBuffer* buffer)
+{
+ vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
+ memset(buffer, 0, sizeof(TPCircularBuffer));
+}
+
+void TPCircularBufferClear(TPCircularBuffer* buffer)
+{
+ int32_t fillCount;
+ if (TPCircularBufferTail(buffer, &fillCount))
+ {
+ TPCircularBufferConsume(buffer, fillCount);
+ }
+}