text: //
  //  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 <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 ) {
          CLOG_DBG("%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 ) {
                  CLOG_DBG("Couldn't map buffer memory to end of buffer\n");
                  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);
      }
  }
  