text: //
  // Copyright (c) 2017 The Khronos Group Inc.
  // 
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //    http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  //
  #include "typeWrappers.h"
  #include "kernelHelpers.h"
  #include "errorHelpers.h"
  #include <stdlib.h>
  #include "clImageHelper.h"
  
  #define ROUND_SIZE_UP( _size, _align )      (((size_t)(_size) + (size_t)(_align) - 1) & -((size_t)(_align)))
  
  #if defined( __APPLE__ )
      #define kPageSize       4096
      #include <sys/mman.h>
      #include <stdlib.h>
  #elif defined(__linux__)
      #include <unistd.h>
      #define kPageSize  (getpagesize())
  #endif
  
  clProtectedImage::clProtectedImage( cl_context context, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width, cl_int *errcode_ret )
  {
      cl_int err = Create( context, mem_flags, fmt, width );
      if( errcode_ret != NULL )
          *errcode_ret = err;
  }
  
  cl_int clProtectedImage::Create( cl_context context, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width )
  {
      cl_int error;
  #if defined( __APPLE__ )
      int protect_pages = 1;
      cl_device_id devices[16];
      size_t number_of_devices;
      error = clGetContextInfo(context, CL_CONTEXT_DEVICES, sizeof(devices), devices, &number_of_devices);
      test_error(error, "clGetContextInfo for CL_CONTEXT_DEVICES failed");
  
      number_of_devices /= sizeof(cl_device_id);
      for (int i=0; i<(int)number_of_devices; i++) {
          cl_device_type type;
          error = clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(type), &type, NULL);
          test_error(error, "clGetDeviceInfo for CL_DEVICE_TYPE failed");
          if (type == CL_DEVICE_TYPE_GPU) {
              protect_pages = 0;
              break;
          }
      }
  
      if (protect_pages) {
          size_t pixelBytes = get_pixel_bytes(fmt);
          size_t rowBytes = ROUND_SIZE_UP( width * pixelBytes, kPageSize );
          size_t rowStride = rowBytes + kPageSize;
  
          // create backing store
          backingStoreSize = rowStride + 8 * rowStride;
          backingStore = mmap(0, backingStoreSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
  
          // add guard pages
          size_t row;
          char *p = (char*) backingStore;
          char *imagePtr = (char*) backingStore + 4 * rowStride;
          for( row = 0; row < 4; row++ )
          {
              mprotect( p, rowStride, PROT_NONE );    p += rowStride;
          }
          p += rowBytes;
          mprotect( p, kPageSize, PROT_NONE );        p += rowStride;
          p -= rowBytes;
          for( row = 0; row < 4; row++ )
          {
              mprotect( p, rowStride, PROT_NONE );    p += rowStride;
          }
  
          if(  getenv( "CL_ALIGN_RIGHT" ) )
          {
              static int spewEnv = 1;
              if(spewEnv)
              {
                  log_info( "***CL_ALIGN_RIGHT is set. Aligning images at right edge of page\n" );
                  spewEnv = 0;
              }
              imagePtr += rowBytes - pixelBytes * width;
          }
  
          image = create_image_1d( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, rowStride, imagePtr, NULL, &error );
      } else {
          backingStore = NULL;
          image = create_image_1d( context, mem_flags, fmt, width, 0, NULL, NULL, &error );
  
      }
  #else
  
      backingStore = NULL;
      image = create_image_1d( context, mem_flags, fmt, width, 0, NULL, NULL, &error );
  
  #endif
      return error;
  }
  
  
  clProtectedImage::clProtectedImage( cl_context context, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width, size_t height, cl_int *errcode_ret )
  {
      cl_int err = Create( context, mem_flags, fmt, width, height );
      if( errcode_ret != NULL )
          *errcode_ret = err;
  }
  
  cl_int clProtectedImage::Create( cl_context context, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width, size_t height )
  {
      cl_int error;
  #if defined( __APPLE__ )
    int protect_pages = 1;
    cl_device_id devices[16];
    size_t number_of_devices;
    error = clGetContextInfo(context, CL_CONTEXT_DEVICES, sizeof(devices), devices, &number_of_devices);
    test_error(error, "clGetContextInfo for CL_CONTEXT_DEVICES failed");
  
    number_of_devices /= sizeof(cl_device_id);
    for (int i=0; i<(int)number_of_devices; i++) {
      cl_device_type type;
      error = clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(type), &type, NULL);
      test_error(error, "clGetDeviceInfo for CL_DEVICE_TYPE failed");
      if (type == CL_DEVICE_TYPE_GPU) {
        protect_pages = 0;
        break;
      }
    }
  
    if (protect_pages) {
      size_t pixelBytes = get_pixel_bytes(fmt);
      size_t rowBytes = ROUND_SIZE_UP( width * pixelBytes, kPageSize );
      size_t rowStride = rowBytes + kPageSize;
  
      // create backing store
      backingStoreSize = height * rowStride + 8 * rowStride;
      backingStore = mmap(0, backingStoreSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
  
      // add guard pages
      size_t row;
      char *p = (char*) backingStore;
      char *imagePtr = (char*) backingStore + 4 * rowStride;
      for( row = 0; row < 4; row++ )
      {
          mprotect( p, rowStride, PROT_NONE );    p += rowStride;
      }
      p += rowBytes;
      for( row = 0; row < height; row++ )
      {
          mprotect( p, kPageSize, PROT_NONE );    p += rowStride;
      }
      p -= rowBytes;
      for( row = 0; row < 4; row++ )
      {
          mprotect( p, rowStride, PROT_NONE );    p += rowStride;
      }
  
      if(  getenv( "CL_ALIGN_RIGHT" ) )
      {
        static int spewEnv = 1;
        if(spewEnv)
        {
          log_info( "***CL_ALIGN_RIGHT is set. Aligning images at right edge of page\n" );
          spewEnv = 0;
        }
        imagePtr += rowBytes - pixelBytes * width;
      }
  
        image = create_image_2d( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, height, rowStride, imagePtr, &error );
    } else {
      backingStore = NULL;
        image = create_image_2d( context, mem_flags, fmt, width, height, 0, NULL, &error );
  
    }
  #else
  
    backingStore = NULL;
    image = create_image_2d( context, mem_flags, fmt, width, height, 0, NULL, &error );
  
  #endif
      return error;
  }
  
  clProtectedImage::clProtectedImage( cl_context context, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width, size_t height, size_t depth, cl_int *errcode_ret )
  {
      cl_int err = Create( context, mem_flags, fmt, width, height, depth );
      if( errcode_ret != NULL )
          *errcode_ret = err;
  }
  
  cl_int clProtectedImage::Create( cl_context context, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width, size_t height, size_t depth )
  {
      cl_int error;
  
  #if defined( __APPLE__ )
    int protect_pages = 1;
    cl_device_id devices[16];
    size_t number_of_devices;
    error = clGetContextInfo(context, CL_CONTEXT_DEVICES, sizeof(devices), devices, &number_of_devices);
    test_error(error, "clGetContextInfo for CL_CONTEXT_DEVICES failed");
  
    number_of_devices /= sizeof(cl_device_id);
    for (int i=0; i<(int)number_of_devices; i++) {
      cl_device_type type;
      error = clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(type), &type, NULL);
      test_error(error, "clGetDeviceInfo for CL_DEVICE_TYPE failed");
      if (type == CL_DEVICE_TYPE_GPU) {
        protect_pages = 0;
        break;
      }
    }
  
    if (protect_pages) {
      size_t pixelBytes = get_pixel_bytes(fmt);
      size_t rowBytes = ROUND_SIZE_UP( width * pixelBytes, kPageSize );
      size_t rowStride = rowBytes + kPageSize;
  
      // create backing store
      backingStoreSize = height * depth * rowStride + 8 * rowStride;
      backingStore = mmap(0, backingStoreSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
  
      // add guard pages
      size_t row;
      char *p = (char*) backingStore;
      char *imagePtr = (char*) backingStore + 4 * rowStride;
      for( row = 0; row < 4; row++ )
      {
          mprotect( p, rowStride, PROT_NONE );    p += rowStride;
      }
      p += rowBytes;
      for( row = 0; row < height*depth; row++ )
      {
          mprotect( p, kPageSize, PROT_NONE );    p += rowStride;
      }
      p -= rowBytes;
      for( row = 0; row < 4; row++ )
      {
          mprotect( p, rowStride, PROT_NONE );    p += rowStride;
      }
  
      if(  getenv( "CL_ALIGN_RIGHT" ) )
      {
          static int spewEnv = 1;
          if(spewEnv)
          {
              log_info( "***CL_ALIGN_RIGHT is set. Aligning images at right edge of page\n" );
              spewEnv = 0;
          }
          imagePtr += rowBytes - pixelBytes * width;
      }
  
      image = create_image_3d( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, height, depth, rowStride, height*rowStride, imagePtr, &error );
    } else {
      backingStore = NULL;
      image = create_image_3d( context, mem_flags, fmt, width, height, depth, 0, 0, NULL, &error );
    }
  #else
  
      backingStore = NULL;
      image = create_image_3d( context, mem_flags, fmt, width, height, depth, 0, 0, NULL, &error );
  
  #endif
  
      return error;
  }
  
  
  clProtectedImage::clProtectedImage( cl_context context, cl_mem_object_type imageType, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width, size_t height, size_t depth, size_t arraySize, cl_int *errcode_ret )
  {
      cl_int err = Create( context, imageType, mem_flags, fmt, width, height, depth, arraySize );
      if( errcode_ret != NULL )
          *errcode_ret = err;
  }
  
  cl_int clProtectedImage::Create( cl_context context, cl_mem_object_type imageType, cl_mem_flags mem_flags, const cl_image_format *fmt, size_t width, size_t height, size_t depth, size_t arraySize )
  {
      cl_int error;
  #if defined( __APPLE__ )
      int protect_pages = 1;
      cl_device_id devices[16];
      size_t number_of_devices;
      error = clGetContextInfo(context, CL_CONTEXT_DEVICES, sizeof(devices), devices, &number_of_devices);
      test_error(error, "clGetContextInfo for CL_CONTEXT_DEVICES failed");
  
      number_of_devices /= sizeof(cl_device_id);
      for (int i=0; i<(int)number_of_devices; i++) {
          cl_device_type type;
          error = clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(type), &type, NULL);
          test_error(error, "clGetDeviceInfo for CL_DEVICE_TYPE failed");
          if (type == CL_DEVICE_TYPE_GPU) {
              protect_pages = 0;
              break;
          }
      }
  
      if (protect_pages) {
          size_t pixelBytes = get_pixel_bytes(fmt);
          size_t rowBytes = ROUND_SIZE_UP( width * pixelBytes, kPageSize );
          size_t rowStride = rowBytes + kPageSize;
  
          // create backing store
          switch (imageType)
          {
              case CL_MEM_OBJECT_IMAGE1D:
                  backingStoreSize = rowStride + 8 * rowStride;
                  break;
              case CL_MEM_OBJECT_IMAGE2D:
                  backingStoreSize = height * rowStride + 8 * rowStride;
                  break;
              case CL_MEM_OBJECT_IMAGE3D:
                  backingStoreSize = height * depth * rowStride + 8 * rowStride;
                  break;
              case CL_MEM_OBJECT_IMAGE1D_ARRAY:
                  backingStoreSize = arraySize * rowStride + 8 * rowStride;
                  break;
              case CL_MEM_OBJECT_IMAGE2D_ARRAY:
                  backingStoreSize = height * arraySize * rowStride + 8 * rowStride;
                  break;
          }
          backingStore = mmap(0, backingStoreSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
  
          // add guard pages
          size_t row;
          char *p = (char*) backingStore;
          char *imagePtr = (char*) backingStore + 4 * rowStride;
          for( row = 0; row < 4; row++ )
          {
              mprotect( p, rowStride, PROT_NONE );    p += rowStride;
          }
          p += rowBytes;
          size_t sz = (height > 0 ? height : 1) * (depth > 0 ? depth : 1) * (arraySize > 0 ? arraySize : 1);
          for( row = 0; row < sz; row++ )
          {
              mprotect( p, kPageSize, PROT_NONE );    p += rowStride;
          }
          p -= rowBytes;
          for( row = 0; row < 4; row++ )
          {
              mprotect( p, rowStride, PROT_NONE );    p += rowStride;
          }
  
          if(  getenv( "CL_ALIGN_RIGHT" ) )
          {
              static int spewEnv = 1;
              if(spewEnv)
              {
                  log_info( "***CL_ALIGN_RIGHT is set. Aligning images at right edge of page\n" );
                  spewEnv = 0;
              }
              imagePtr += rowBytes - pixelBytes * width;
          }
  
          switch (imageType)
          {
              case CL_MEM_OBJECT_IMAGE1D:
                  image = create_image_1d( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, rowStride, imagePtr, NULL, &error );
                  break;
              case CL_MEM_OBJECT_IMAGE2D:
                  image = create_image_2d( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, height, rowStride, imagePtr, &error );
                  break;
              case CL_MEM_OBJECT_IMAGE3D:
                  image = create_image_3d( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, height, depth, rowStride, height*rowStride, imagePtr, &error );
                  break;
              case CL_MEM_OBJECT_IMAGE1D_ARRAY:
                  image = create_image_1d_array( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, arraySize, rowStride, rowStride, imagePtr, &error );
                  break;
              case CL_MEM_OBJECT_IMAGE2D_ARRAY:
                  image = create_image_2d_array( context, mem_flags | CL_MEM_USE_HOST_PTR, fmt, width, height, arraySize, rowStride, height*rowStride, imagePtr, &error );
                  break;
          }
      } else {
          backingStore = NULL;
          switch (imageType)
          {
              case CL_MEM_OBJECT_IMAGE1D:
                  image = create_image_1d( context, mem_flags, fmt, width, 0, NULL, NULL, &error );
                  break;
              case CL_MEM_OBJECT_IMAGE2D:
                  image = create_image_2d( context, mem_flags, fmt, width, height, 0, NULL, &error );
                  break;
              case CL_MEM_OBJECT_IMAGE3D:
                  image = create_image_3d( context, mem_flags, fmt, width, height, depth, 0, 0, NULL, &error );;
                  break;
              case CL_MEM_OBJECT_IMAGE1D_ARRAY:
                  image = create_image_1d_array( context, mem_flags, fmt, width, arraySize, 0, 0, NULL, &error );
                  break;
              case CL_MEM_OBJECT_IMAGE2D_ARRAY:
                  image = create_image_2d_array( context, mem_flags, fmt, width, height, arraySize, 0, 0, NULL, &error );
                  break;
          }
  
      }
  #else
  
      backingStore = NULL;
      switch (imageType)
      {
          case CL_MEM_OBJECT_IMAGE1D:
              image = create_image_1d( context, mem_flags, fmt, width, 0, NULL, NULL, &error );
              break;
          case CL_MEM_OBJECT_IMAGE2D:
              image = create_image_2d( context, mem_flags, fmt, width, height, 0, NULL, &error );
              break;
          case CL_MEM_OBJECT_IMAGE3D:
              image = create_image_3d( context, mem_flags, fmt, width, height, depth, 0, 0, NULL, &error );;
              break;
          case CL_MEM_OBJECT_IMAGE1D_ARRAY:
              image = create_image_1d_array( context, mem_flags, fmt, width, arraySize, 0, 0, NULL, &error );
              break;
          case CL_MEM_OBJECT_IMAGE2D_ARRAY:
              image = create_image_2d_array( context, mem_flags, fmt, width, height, arraySize, 0, 0, NULL, &error );
              break;
      }
  #endif
      return error;
  }
  
  
  
  /*******
   * clProtectedArray implementation
   *******/
  clProtectedArray::clProtectedArray()
  {
      mBuffer = mValidBuffer = NULL;
  }
  
  clProtectedArray::clProtectedArray( size_t sizeInBytes )
  {
      mBuffer = mValidBuffer = NULL;
      Allocate( sizeInBytes );
  }
  
  clProtectedArray::~clProtectedArray()
  {
      if( mBuffer != NULL ) {
  #if defined( __APPLE__ )
          int error = munmap( mBuffer, mRealSize );
        if (error) log_error("WARNING: munmap failed in clProtectedArray.\n");
  #else
      free( mBuffer );
  #endif
    }
  }
  
  void clProtectedArray::Allocate( size_t sizeInBytes )
  {
  
  #if defined( __APPLE__ )
  
      // Allocate enough space to: round up our actual allocation to an even number of pages
      // and allocate two pages on either side
      mRoundedSize = ROUND_SIZE_UP( sizeInBytes, kPageSize );
      mRealSize = mRoundedSize + kPageSize * 2;
  
      // Use mmap here to ensure we start on a page boundary, so the mprotect calls will work OK
      mBuffer = (char *)mmap(0, mRealSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
  
      mValidBuffer = mBuffer + kPageSize;
  
      // Protect guard area from access
      mprotect( mValidBuffer - kPageSize, kPageSize, PROT_NONE );
      mprotect( mValidBuffer + mRoundedSize, kPageSize, PROT_NONE );
  #else
    mRoundedSize = mRealSize = sizeInBytes;
    mBuffer = mValidBuffer = (char *)calloc(1, mRealSize);
  #endif
  }
  
  
  