<commit_before>/*
* OS and machine specific utility functions
* (C) 2015,2016,2017 Jack Lloyd
* (C) 2016 Daniel Neus
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/os_utils.h>
#include <botan/cpuid.h>
#include <botan/exceptn.h>
#include <botan/mem_ops.h>

#include <chrono>
#include <cstdlib>

#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
  #include <string.h>
#endif

#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
  #include <sys/types.h>
  #include <sys/resource.h>
  #include <sys/mman.h>
  #include <signal.h>
  #include <setjmp.h>
  #include <unistd.h>
  #include <errno.h>
#elif defined(BOTAN_TARGET_OS_TYPE_IS_WINDOWS)
  #define NOMINMAX 1
  #include <windows.h>
#endif

namespace Botan {

// Not defined in OS namespace for historical reasons
void secure_scrub_memory(void* ptr, size_t n)
   {
#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
   ::RtlSecureZeroMemory(ptr, n);

#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
   ::explicit_bzero(ptr, n);

#elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1)
   /*
   Call memset through a static volatile pointer, which the compiler
   should not elide. This construct should be safe in conforming
   compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and
   Clang 3.8 both create code that saves the memset address in the
   data segment and uncondtionally loads and jumps to that address.
   */
   static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
   (memset_ptr)(ptr, 0, n);
#else

   volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr);

   for(size_t i = 0; i != n; ++i)
      p[i] = 0;
#endif
   }

uint32_t OS::get_process_id()
   {
#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
   return ::getpid();
#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW)
   return ::GetCurrentProcessId();
#elif defined(BOTAN_TARGET_OS_TYPE_IS_UNIKERNEL) || defined(BOTAN_TARGET_OS_IS_LLVM)
   return 0; // truly no meaningful value
#else
   #error "Missing get_process_id"
#endif
   }

uint64_t OS::get_processor_timestamp()
   {
   uint64_t rtc = 0;

#if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER)
   LARGE_INTEGER tv;
   ::QueryPerformanceCounter(&tv);
   rtc = tv.QuadPart;

#elif defined(BOTAN_USE_GCC_INLINE_ASM)

#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)

   if(CPUID::has_rdtsc())
      {
      uint32_t rtc_low = 0, rtc_high = 0;
      asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
      rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
      }

#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
   uint32_t rtc_low = 0, rtc_high = 0;
   asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low));

   /*
   qemu-ppc seems to not support mftb instr, it always returns zero.
   If both time bases are 0, assume broken and return another clock.
   */
   if(rtc_high > 0 || rtc_low > 0)
      {
      rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
      }

#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
   asm volatile("rpcc %0" : "=r" (rtc));

   // OpenBSD does not trap access to the %tick register
#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
   asm volatile("rd %%tick, %0" : "=r" (rtc));

#elif defined(BOTAN_TARGET_ARCH_IS_IA64)
   asm volatile("mov %0=ar.itc" : "=r" (rtc));

#elif defined(BOTAN_TARGET_ARCH_IS_S390X)
   asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");

#elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
   asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?

#else
   //#warning "OS::get_processor_timestamp not implemented"
#endif

#endif

   return rtc;
   }

uint64_t OS::get_high_resolution_clock()
   {
   if(uint64_t cpu_clock = OS::get_processor_timestamp())
      return cpu_clock;

   /*
   If we got here either we either don't have an asm instruction
   above, or (for x86) RDTSC is not available at runtime. Try some
   clock_gettimes and return the first one that works, or otherwise
   fall back to std::chrono.
   */

#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)

   // The ordering here is somewhat arbitrary...
   const clockid_t clock_types[] = {
#if defined(CLOCK_MONOTONIC_HR)
      CLOCK_MONOTONIC_HR,
#endif
#if defined(CLOCK_MONOTONIC_RAW)
      CLOCK_MONOTONIC_RAW,
#endif
#if defined(CLOCK_MONOTONIC)
      CLOCK_MONOTONIC,
#endif
#if defined(CLOCK_PROCESS_CPUTIME_ID)
      CLOCK_PROCESS_CPUTIME_ID,
#endif
#if defined(CLOCK_THREAD_CPUTIME_ID)
      CLOCK_THREAD_CPUTIME_ID,
#endif
   };

   for(clockid_t clock : clock_types)
      {
      struct timespec ts;
      if(::clock_gettime(clock, &ts) == 0)
         {
         return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
         }
      }
#endif

   // Plain C++11 fallback
   auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
   }

uint64_t OS::get_system_timestamp_ns()
   {
#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
   struct timespec ts;
   if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
      {
      return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
      }
#endif

   auto now = std::chrono::system_clock::now().time_since_epoch();
   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
   }

size_t OS::get_memory_locking_limit()
   {
#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
   /*
   * Linux defaults to only 64 KiB of mlockable memory per process
   * (too small) but BSDs offer a small fraction of total RAM (more
   * than we need). Bound the total mlock size to 512 KiB which is
   * enough to run the entire test suite without spilling to non-mlock
   * memory (and thus presumably also enough for many useful
   * programs), but small enough that we should not cause problems
   * even if many processes are mlocking on the same machine.
   */
   size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB;

   /*
   * Allow override via env variable
   */
   if(const char* env = ::getenv("BOTAN_MLOCK_POOL_SIZE"))
      {
      try
         {
         const size_t user_req = std::stoul(env, nullptr);
         mlock_requested = std::min(user_req, mlock_requested);
         }
      catch(std::exception&) { /* ignore it */ }
      }

#if defined(RLIMIT_MEMLOCK)
   if(mlock_requested > 0)
      {
      struct ::rlimit limits;

      ::getrlimit(RLIMIT_MEMLOCK, &limits);

      if(limits.rlim_cur < limits.rlim_max)
         {
         limits.rlim_cur = limits.rlim_max;
         ::setrlimit(RLIMIT_MEMLOCK, &limits);
         ::getrlimit(RLIMIT_MEMLOCK, &limits);
         }

      return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
      }
#else
   /*
   * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
   * unprivileged mlock calls.
   */
   return 0;
#endif

#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
   SIZE_T working_min = 0, working_max = 0;
   DWORD working_flags = 0;
   if(!::GetProcessWorkingSetSizeEx(::GetCurrentProcess(), &working_min, &working_max, &working_flags))
      {
      return 0;
      }

   SYSTEM_INFO sSysInfo;
   ::GetSystemInfo(&sSysInfo);

   // According to Microsoft MSDN:
   // The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead
   // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
   // But the information in the book seems to be inaccurate/outdated
   // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
   // On all three OS the value is 11 instead of 8
   size_t overhead = sSysInfo.dwPageSize * 11ULL;
   if(working_min > overhead)
      {
      size_t lockable_bytes = working_min - overhead;
      if(lockable_bytes < (BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL))
         {
         return lockable_bytes;
         }
      else
         {
         return BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL;
         }
      }
#endif

   return 0;
   }

void* OS::allocate_locked_pages(size_t length)
   {
#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)

#if !defined(MAP_NOCORE)
   #define MAP_NOCORE 0
#endif

#if !defined(MAP_ANONYMOUS)
   #define MAP_ANONYMOUS MAP_ANON
#endif

   void* ptr = ::mmap(nullptr,
                      length,
                      PROT_READ | PROT_WRITE,
                      MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
                      /*fd*/-1,
                      /*offset*/0);

   if(ptr == MAP_FAILED)
      {
      return nullptr;
      }

#if defined(MADV_DONTDUMP)
   ::madvise(ptr, length, MADV_DONTDUMP);
#endif

   if(::mlock(ptr, length) != 0)
      {
      ::munmap(ptr, length);
      return nullptr; // failed to lock
      }

   ::memset(ptr, 0, length);

   return ptr;
#elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
   LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
   if(!ptr)
      {
      return nullptr;
      }

   if(::VirtualLock(ptr, length) == 0)
      {
      ::VirtualFree(ptr, 0, MEM_RELEASE);
      return nullptr; // failed to lock
      }

   return ptr;
#else
   BOTAN_UNUSED(length);
   return nullptr; /* not implemented */
#endif
   }

void OS::free_locked_pages(void* ptr, size_t length)
   {
   if(ptr == nullptr || length == 0)
      return;

#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
   secure_scrub_memory(ptr, length);
   ::munlock(ptr, length);
   ::munmap(ptr, length);
#elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
   secure_scrub_memory(ptr, length);
   ::VirtualUnlock(ptr, length);
   ::VirtualFree(ptr, 0, MEM_RELEASE);
#else
   // Invalid argument because no way this pointer was allocated by us
   throw Invalid_Argument("Invalid ptr to free_locked_pages");
#endif
   }

#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
namespace {

static ::sigjmp_buf g_sigill_jmp_buf;

void botan_sigill_handler(int)
   {
   ::siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
   }

}
#endif

int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
   {
   volatile int probe_result = -3;

#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
   struct sigaction old_sigaction;
   struct sigaction sigaction;

   sigaction.sa_handler = botan_sigill_handler;
   sigemptyset(&sigaction.sa_mask);
   sigaction.sa_flags = 0;

   int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);

   if(rc != 0)
      throw Exception("run_cpu_instruction_probe sigaction failed");

   rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);

   if(rc == 0)
      {
      // first call to sigsetjmp
      probe_result = probe_fn();
      }
   else if(rc == 1)
      {
      // non-local return from siglongjmp in signal handler: return error
      probe_result = -1;
      }

   // Restore old SIGILL handler, if any
   rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
   if(rc != 0)
      throw Exception("run_cpu_instruction_probe sigaction restore failed");

#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)

   // Windows SEH
   __try
      {
      probe_result = probe_fn();
      }
   __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
      {
      probe_result = -1;
      }

#endif

   return probe_result;
   }

}
<commit_msg>getenv is in standard C++<commit_after>/*
* OS and machine specific utility functions
* (C) 2015,2016,2017 Jack Lloyd
* (C) 2016 Daniel Neus
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/os_utils.h>
#include <botan/cpuid.h>
#include <botan/exceptn.h>
#include <botan/mem_ops.h>

#include <chrono>
#include <cstdlib>

#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
  #include <string.h>
#endif

#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
  #include <sys/types.h>
  #include <sys/resource.h>
  #include <sys/mman.h>
  #include <signal.h>
  #include <setjmp.h>
  #include <unistd.h>
  #include <errno.h>
#elif defined(BOTAN_TARGET_OS_TYPE_IS_WINDOWS)
  #define NOMINMAX 1
  #include <windows.h>
#endif

namespace Botan {

// Not defined in OS namespace for historical reasons
void secure_scrub_memory(void* ptr, size_t n)
   {
#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
   ::RtlSecureZeroMemory(ptr, n);

#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
   ::explicit_bzero(ptr, n);

#elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1)
   /*
   Call memset through a static volatile pointer, which the compiler
   should not elide. This construct should be safe in conforming
   compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and
   Clang 3.8 both create code that saves the memset address in the
   data segment and uncondtionally loads and jumps to that address.
   */
   static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
   (memset_ptr)(ptr, 0, n);
#else

   volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr);

   for(size_t i = 0; i != n; ++i)
      p[i] = 0;
#endif
   }

uint32_t OS::get_process_id()
   {
#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
   return ::getpid();
#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW)
   return ::GetCurrentProcessId();
#elif defined(BOTAN_TARGET_OS_TYPE_IS_UNIKERNEL) || defined(BOTAN_TARGET_OS_IS_LLVM)
   return 0; // truly no meaningful value
#else
   #error "Missing get_process_id"
#endif
   }

uint64_t OS::get_processor_timestamp()
   {
   uint64_t rtc = 0;

#if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER)
   LARGE_INTEGER tv;
   ::QueryPerformanceCounter(&tv);
   rtc = tv.QuadPart;

#elif defined(BOTAN_USE_GCC_INLINE_ASM)

#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)

   if(CPUID::has_rdtsc())
      {
      uint32_t rtc_low = 0, rtc_high = 0;
      asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
      rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
      }

#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
   uint32_t rtc_low = 0, rtc_high = 0;
   asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low));

   /*
   qemu-ppc seems to not support mftb instr, it always returns zero.
   If both time bases are 0, assume broken and return another clock.
   */
   if(rtc_high > 0 || rtc_low > 0)
      {
      rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
      }

#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
   asm volatile("rpcc %0" : "=r" (rtc));

   // OpenBSD does not trap access to the %tick register
#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
   asm volatile("rd %%tick, %0" : "=r" (rtc));

#elif defined(BOTAN_TARGET_ARCH_IS_IA64)
   asm volatile("mov %0=ar.itc" : "=r" (rtc));

#elif defined(BOTAN_TARGET_ARCH_IS_S390X)
   asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");

#elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
   asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?

#else
   //#warning "OS::get_processor_timestamp not implemented"
#endif

#endif

   return rtc;
   }

uint64_t OS::get_high_resolution_clock()
   {
   if(uint64_t cpu_clock = OS::get_processor_timestamp())
      return cpu_clock;

   /*
   If we got here either we either don't have an asm instruction
   above, or (for x86) RDTSC is not available at runtime. Try some
   clock_gettimes and return the first one that works, or otherwise
   fall back to std::chrono.
   */

#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)

   // The ordering here is somewhat arbitrary...
   const clockid_t clock_types[] = {
#if defined(CLOCK_MONOTONIC_HR)
      CLOCK_MONOTONIC_HR,
#endif
#if defined(CLOCK_MONOTONIC_RAW)
      CLOCK_MONOTONIC_RAW,
#endif
#if defined(CLOCK_MONOTONIC)
      CLOCK_MONOTONIC,
#endif
#if defined(CLOCK_PROCESS_CPUTIME_ID)
      CLOCK_PROCESS_CPUTIME_ID,
#endif
#if defined(CLOCK_THREAD_CPUTIME_ID)
      CLOCK_THREAD_CPUTIME_ID,
#endif
   };

   for(clockid_t clock : clock_types)
      {
      struct timespec ts;
      if(::clock_gettime(clock, &ts) == 0)
         {
         return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
         }
      }
#endif

   // Plain C++11 fallback
   auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
   }

uint64_t OS::get_system_timestamp_ns()
   {
#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
   struct timespec ts;
   if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
      {
      return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
      }
#endif

   auto now = std::chrono::system_clock::now().time_since_epoch();
   return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
   }

size_t OS::get_memory_locking_limit()
   {
#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
   /*
   * Linux defaults to only 64 KiB of mlockable memory per process
   * (too small) but BSDs offer a small fraction of total RAM (more
   * than we need). Bound the total mlock size to 512 KiB which is
   * enough to run the entire test suite without spilling to non-mlock
   * memory (and thus presumably also enough for many useful
   * programs), but small enough that we should not cause problems
   * even if many processes are mlocking on the same machine.
   */
   size_t mlock_requested = BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB;

   /*
   * Allow override via env variable
   */
   if(const char* env = std::getenv("BOTAN_MLOCK_POOL_SIZE"))
      {
      try
         {
         const size_t user_req = std::stoul(env, nullptr);
         mlock_requested = std::min(user_req, mlock_requested);
         }
      catch(std::exception&) { /* ignore it */ }
      }

#if defined(RLIMIT_MEMLOCK)
   if(mlock_requested > 0)
      {
      struct ::rlimit limits;

      ::getrlimit(RLIMIT_MEMLOCK, &limits);

      if(limits.rlim_cur < limits.rlim_max)
         {
         limits.rlim_cur = limits.rlim_max;
         ::setrlimit(RLIMIT_MEMLOCK, &limits);
         ::getrlimit(RLIMIT_MEMLOCK, &limits);
         }

      return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
      }
#else
   /*
   * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
   * unprivileged mlock calls.
   */
   return 0;
#endif

#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
   SIZE_T working_min = 0, working_max = 0;
   DWORD working_flags = 0;
   if(!::GetProcessWorkingSetSizeEx(::GetCurrentProcess(), &working_min, &working_max, &working_flags))
      {
      return 0;
      }

   SYSTEM_INFO sSysInfo;
   ::GetSystemInfo(&sSysInfo);

   // According to Microsoft MSDN:
   // The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead
   // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
   // But the information in the book seems to be inaccurate/outdated
   // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
   // On all three OS the value is 11 instead of 8
   size_t overhead = sSysInfo.dwPageSize * 11ULL;
   if(working_min > overhead)
      {
      size_t lockable_bytes = working_min - overhead;
      if(lockable_bytes < (BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL))
         {
         return lockable_bytes;
         }
      else
         {
         return BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024ULL;
         }
      }
#endif

   return 0;
   }

void* OS::allocate_locked_pages(size_t length)
   {
#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)

#if !defined(MAP_NOCORE)
   #define MAP_NOCORE 0
#endif

#if !defined(MAP_ANONYMOUS)
   #define MAP_ANONYMOUS MAP_ANON
#endif

   void* ptr = ::mmap(nullptr,
                      length,
                      PROT_READ | PROT_WRITE,
                      MAP_ANONYMOUS | MAP_SHARED | MAP_NOCORE,
                      /*fd*/-1,
                      /*offset*/0);

   if(ptr == MAP_FAILED)
      {
      return nullptr;
      }

#if defined(MADV_DONTDUMP)
   ::madvise(ptr, length, MADV_DONTDUMP);
#endif

   if(::mlock(ptr, length) != 0)
      {
      ::munmap(ptr, length);
      return nullptr; // failed to lock
      }

   ::memset(ptr, 0, length);

   return ptr;
#elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
   LPVOID ptr = ::VirtualAlloc(nullptr, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
   if(!ptr)
      {
      return nullptr;
      }

   if(::VirtualLock(ptr, length) == 0)
      {
      ::VirtualFree(ptr, 0, MEM_RELEASE);
      return nullptr; // failed to lock
      }

   return ptr;
#else
   BOTAN_UNUSED(length);
   return nullptr; /* not implemented */
#endif
   }

void OS::free_locked_pages(void* ptr, size_t length)
   {
   if(ptr == nullptr || length == 0)
      return;

#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
   secure_scrub_memory(ptr, length);
   ::munlock(ptr, length);
   ::munmap(ptr, length);
#elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK
   secure_scrub_memory(ptr, length);
   ::VirtualUnlock(ptr, length);
   ::VirtualFree(ptr, 0, MEM_RELEASE);
#else
   // Invalid argument because no way this pointer was allocated by us
   throw Invalid_Argument("Invalid ptr to free_locked_pages");
#endif
   }

#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
namespace {

static ::sigjmp_buf g_sigill_jmp_buf;

void botan_sigill_handler(int)
   {
   ::siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
   }

}
#endif

int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
   {
   volatile int probe_result = -3;

#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
   struct sigaction old_sigaction;
   struct sigaction sigaction;

   sigaction.sa_handler = botan_sigill_handler;
   sigemptyset(&sigaction.sa_mask);
   sigaction.sa_flags = 0;

   int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);

   if(rc != 0)
      throw Exception("run_cpu_instruction_probe sigaction failed");

   rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);

   if(rc == 0)
      {
      // first call to sigsetjmp
      probe_result = probe_fn();
      }
   else if(rc == 1)
      {
      // non-local return from siglongjmp in signal handler: return error
      probe_result = -1;
      }

   // Restore old SIGILL handler, if any
   rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
   if(rc != 0)
      throw Exception("run_cpu_instruction_probe sigaction restore failed");

#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)

   // Windows SEH
   __try
      {
      probe_result = probe_fn();
      }
   __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
      {
      probe_result = -1;
      }

#endif

   return probe_result;
   }

}
<|endoftext|>


<commit_before>/**
 * KVH 1750 Base Interface
 * \author: Jason Ziglar <jpz@vt.edu>
 * \date: 02/15/2015
 * Copyright 2015, Virginia Tech. All Rights Reserved.
 */
#include "kvh1750/imu.h"

#include <algorithm>

#include <sstream>

namespace kvh
{

namespace
{
  const std::string RFCmd = "ROTFMT";
  const std::string DRCmd = "DR";
  const std::string TUCmd = "TEMPUNITS";
  const std::string ConfigCmd = "CONFIG";
}

const std::vector<int> IMU1750::DataRates {1, 5, 10, 25, 50, 100, 250, 500, 750, 1000};

/**
 * Default constructor
 */
IMU1750::IMU1750(std::shared_ptr<IOModule> mod)
  :_io(mod),
  _buff(sizeof(kvh::RawMessage), 0),
  _bytes_read(0),
  _rate(IMU1750::DataRates.back()),
  _max_temp(kvh::MaxTemp_C),
  _is_config(false),
  _is_c(true),
  _is_da(true)
{
  query_temp_units(_is_c);
  query_angle_units(_is_da);
  query_data_rate(_rate);
}

/**
 * Default destructor
 */
IMU1750::~IMU1750()
{
}

/**
 * Sets temperature limit over which over temp is reported
 */
void IMU1750::set_temp_limit(int max_temp)
{
  _max_temp = max_temp;
}

/**
 * Public read interface, which attempts to read a new KVH message
 * \param[in] msg Storage location for new message.
 * \param[out] size_t Flag indicating read success.
 */
typename IMU1750::ParseResults IMU1750::read(kvh::Message& msg)
{
  if(!base_read())
  {
    return BAD_READ;
  }
  std::vector<char>::iterator match = _buff.end();
  bool header_start = find_header(true, match);

  IMU1750::ParseResults result = VALID;

  if(header_start && _bytes_read == sizeof(kvh::RawMessage))
  {
    //get timestamp from reading
    uint64_t secs = 0;
    uint64_t nsecs = 0;
    _io->time(secs, nsecs);
    kvh::RawMessage* raw = reinterpret_cast<kvh::RawMessage*>(&_buff.front());
    result = (msg.from_raw(*raw, secs, nsecs, _is_c, _is_da) ? VALID : BAD_CRC);
    //cleanup from successful read
    reset_buffer();

    bool celsius;
    int temp = msg.temp(celsius);
    if(!celsius)
    {
      kvh::to_c(temp);
    }
    if(temp >= _max_temp)
    {
      result = OVER_TEMP;
    }
  }
  else
  {
    if(match != _buff.begin())
    {
      reset_partial_buffer(match);
    }
    result = PARTIAL_READ;
  }

  return result;
}

/**
 * Reports last read IMU read rate for general use. This does not
 * stop data from the IMU.
 * \param[out] Rate in Hz
 */
int IMU1750::imu_rate() const
{
  return _rate;
}

/**
 * Reports last read temperature units from IMU. This does not stop
 * data from the IMU.
 * \param[out] Flag indicating if temperatures are in Celsius
 */
bool IMU1750::is_celsius() const
{
  return _is_c;
}

/**
 * Queries the IMU to determine the temperature units.
 * \param[in,out] is_celsius Flag set to indicate if temperature is in C
 * \param[out] Flag indicating if the query came back valid
 */
bool IMU1750::query_temp_units(bool& celsius)
{
  std::string cmd = build_command(TUCmd, "", true);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(TUCmd, match) && parse_temp_units(match);
    if(status)
    {
      celsius = _is_c;
    }
  }

  set_mode(false);
  return status;
}

/**
 * Queries IMU to determine the rotational units.
 * \param[in,out] Flag indicating if angles are raw (delta angles) or filtered
 * to be an angular rate
 * \param[out] Flag indicating if query was processed.
 */
bool IMU1750::query_angle_units(bool& is_da)
{
  std::string cmd = build_command(RFCmd, "", true);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(RFCmd, match);
    if(status)
    {
      status = parse_angle_units(match);
    }
  }

  set_mode(false);
  return status;
}

/**
 * Query IMU for current reporting data rate.
 * \param[in,out] rate_hz Reporting rate in Hz.
 * \param[out] Flag indicating if query succeeded.
 */
bool IMU1750::query_data_rate(int& rate_hz)
{
  std::string cmd = build_command(DRCmd, "", true);
  set_mode(true);
  cmd_write(cmd);
  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(DRCmd, match) &&
      parse_data_rate(match, rate_hz);
  }
  set_mode(false);
  return status;
}

/**
 * Set temperature units (C or F)
 * \param[in] Flag indicating if temperature is in C
 * \param[out] Flag indicating if command succeeded
 */
bool IMU1750::set_temp_units(bool celsius)
{
  const std::string val = (celsius ? "C" : "F");
  std::string cmd = build_command(TUCmd, val, false);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(TUCmd, match) && parse_temp_units(match);
  }
  set_mode(false);
  return status;
}

/**
 * Set reporting data rate for IMU.
 * \param[in] Rate in Hz. Must be one of the options in IMU1750::DataRates
 * \param[out] Flag indicating if command succeeded
 */
bool IMU1750::set_data_rate(int rate_hz)
{
  std::vector<int>::const_iterator dr_match = std::find(IMU1750::DataRates.begin(),
    IMU1750::DataRates.end(), rate_hz);
  if(dr_match == IMU1750::DataRates.end())
  {
    return false;
  }

  const std::string val = std::to_string(rate_hz);
  std::string cmd = build_command(DRCmd, val, false);

  set_mode(true);
  cmd_write(cmd);

  std::vector<char>::iterator match;
  bool status = cmd_read() && find_response(DRCmd, match);

  set_mode(false);
  return status;
}

/**
 * Set angle units for IMU.
 * \param[in] Flag indicating if units are raw (delta angles) or not
 * \param[out] Flag indicating if command succeeded.
 */
bool IMU1750::set_angle_units(bool is_da)
{
  const std::string val = (is_da ? "DELTA" : "RATE");
  std::string cmd = build_command(RFCmd, val, false);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(RFCmd, match);
    if(status)
    {
      status = parse_angle_units(match);
    }
  }
  set_mode(false);
  return status;
}

/**
 * Sets the IMU mode to either normal or config.
 * \param[in] Flag indicating if IMU should enter config mode.
 */
void IMU1750::set_mode(bool enter_config)
{
  if(enter_config == _is_config)
  {
    return;
  }

  std::string val = (enter_config ? "1" : "0");
  std::string cmd = build_command(ConfigCmd, val, false);
  cmd_write(cmd);

  //normal mode never responds with confirmation
  if(enter_config)
  {
    reset_buffer();
    cmd_read();
  }

  _is_config = enter_config;
}

/**
 * Parse buffer for angle units.
 * \param[in] Iterator pointing at start of response parameters
 * \param[out] Flag indicating if response was correctly parsed
 */
bool IMU1750::parse_angle_units(std::vector<char>::iterator match)
{
  bool status = false;
  const std::string Delta = "DELTA";
  const std::string Rate = "RATE";
  if(std::strncmp(&*match, &*Delta.begin(), Delta.length()) == 0)
  {
    _is_da = true;
    status = true;
  }
  else if(std::strncmp(&*match, &*Rate.begin(), Rate.length()) == 0)
  {
    _is_da = false;
    status = true;
  }

  return status;
}

/**
 * Parse buffer for temperature units
 * \param[in] Iterator pointing at start of response parameters
 * \param[out] Flag indicating if response was correctly parsed
 */
bool IMU1750::parse_temp_units(std::vector<char>::iterator match)
{
  bool status = true;
  switch(*match)
  {
    case 'C':
      _is_c = true;
      break;
    case 'F':
      _is_c = false;
      break;
    default:
      status = false;
      break;
  }

  return status;
}

/**
 * Parse buffer for data rate
 * \param[in] Iterator pointing at start of response parameters
 * \param[in,out] Rate value parsed
 * \param[out] Flag indicating if response was correctly parsed
 */
bool IMU1750::parse_data_rate(std::vector<char>::iterator match, int& rate)
{
  rate = std::strtol(&*match, NULL, 10);
  return true;
}

/**
 * Internal read function which performs read and maintains internal state.
 * \param[in] use_tov Flag indicating if the timing signal should be used.
 * \param[out] Flag indicating if read was valid.
 */
bool IMU1750::base_read(bool use_tov)
{
  size_t bytes = 0;
  bool status = _io->read(&_buff.front() + _bytes_read, bytes_remaining(), bytes, use_tov);
  if(!status)
  {
    reset_buffer();
  }
  else
  {
    _bytes_read += bytes;
  }
  return status;
}

/**
 * Attempt to read response from command.
 * \param[out] Flag indicating if buffer now contains the response
 */
bool IMU1750::cmd_read()
{
  const size_t MaxReadAttempts = 20;

  bool response = false;
  for(size_t ii = 0; !response && ii < MaxReadAttempts; ++ii)
  {
    base_read(false);

    if(_buff[_bytes_read - 1] == '\n')
    {
      response = true;
    }
    else if(bytes_remaining() == 0)
    {
      reset_buffer();
    }
  }

  return response;
}

/**
 * Write command to IMU.
 */
void IMU1750::cmd_write(const std::string& cmd)
{
  _io->flush_buffers();
  reset_buffer();
  _io->write(cmd.c_str(), cmd.length());
}

/**
 * Function to search the memory buffer for the header sequence.
 * \param[in,out] iterator pointing to location of header
 * \param[out] Flag indicating if header is at the start of the header.
 */
bool IMU1750::find_header(bool is_imu, std::vector<char>::iterator& match)
{
  std::vector<char>::iterator buffer_end = _buff.begin() + _bytes_read;
  HeaderType::const_iterator header_start = (is_imu ? IMUHeader.begin() : BITHeader.begin());
  HeaderType::const_iterator header_end = (is_imu ? IMUHeader.end() : BITHeader.end());
  match = std::search(_buff.begin(), buffer_end, header_start, header_end);

  return match == _buff.begin();
}

/**
 * Clears and resets the memory buffer used to store the raw message being
 * processed.
 */
void IMU1750::reset_buffer()
{
  _buff.clear();
  _buff.resize(_buff.capacity(), 0);
  _bytes_read = 0;
  _io->reset_time();
}

/**
 * Stores a partially read message at the start of the buffer
 * to simplify processing.
 * \param[in] match Iterator pointing to start of header.
 */
void IMU1750::reset_partial_buffer(const std::vector<char>::iterator& match)
{
  std::vector<char>::iterator buffer_end = _buff.begin() + _bytes_read;
  //start at either the match, or the longest missable substring
  std::vector<char>::iterator start = std::min(match, std::max(_buff.begin(),
    buffer_end - (HeaderSize - 1)));
  std::vector<char> new_buffer(start, buffer_end);
  new_buffer.resize(_buff.capacity(), 0);
  _bytes_read = buffer_end - start;
  _buff.swap(new_buffer);
}

/**
 * Number of bytes available for writing in the buffer.
 */
size_t IMU1750::bytes_remaining() const
{
  return _buff.size() - _bytes_read;
}

/**
 * Sets the buffer size in case larger messages need to be read.
 * \param[in] len Length of internal buffer in bytes
 */
void IMU1750::set_buffer_size(size_t len)
{
  std::vector<char>::reverse_iterator start = std::max(_buff.rend(),
    _buff.rbegin() + len);
  std::vector<char> new_buffer(start, _buff.rend()); //copy last len bytes
  new_buffer.resize(len, 0); //make sure size is correct
  _buff.swap(new_buffer);
}

/**
 * Utility function for building an IMU commands in a consistent fashion.
 * \param[in] Command type.
 * \param[in] Command arguments, if any are needed.
 * \param[in] Flag indicating if command is a query or not.
 * \param[out] String containing the fully built command
 */
std::string IMU1750::build_command(const std::string& type, const std::string& val,
  bool is_query)
{
  std::stringstream ss;
  ss << (is_query ? "?" : "=") << type;
  if(!is_query)
  {
    ss << "," << val;
  }
  ss << "\n";
  return ss.str();
}

/**
 * Utility function to attempt to find a valid response in the internal buffer.
 * \param[in] Command type to search for
 * \param[in,out] Iterator pointing at start of response arguments
 * \pram[out] Flag indicating if response was found
 */
bool IMU1750::find_response(const std::string& type,
  std::vector<char>::iterator& match)
{
  std::string FullMatch = type + ",";
  match = std::search(_buff.begin(), _buff.end(),
    FullMatch.begin(), FullMatch.end());

  bool status = (match != _buff.end());
  match += FullMatch.length();
  return status;
}

}<commit_msg>Now exiting config mode at startup.<commit_after>/**
 * KVH 1750 Base Interface
 * \author: Jason Ziglar <jpz@vt.edu>
 * \date: 02/15/2015
 * Copyright 2015, Virginia Tech. All Rights Reserved.
 */
#include "kvh1750/imu.h"

#include <algorithm>

#include <sstream>

namespace kvh
{

namespace
{
  const std::string RFCmd = "ROTFMT";
  const std::string DRCmd = "DR";
  const std::string TUCmd = "TEMPUNITS";
  const std::string ConfigCmd = "CONFIG";
}

const std::vector<int> IMU1750::DataRates {1, 5, 10, 25, 50, 100, 250, 500, 750, 1000};

/**
 * Default constructor
 */
IMU1750::IMU1750(std::shared_ptr<IOModule> mod)
  :_io(mod),
  _buff(sizeof(kvh::RawMessage), 0),
  _bytes_read(0),
  _rate(IMU1750::DataRates.back()),
  _max_temp(kvh::MaxTemp_C),
  _is_config(true),
  _is_c(true),
  _is_da(true)
{
  set_mode(false);
  query_temp_units(_is_c);
  query_angle_units(_is_da);
  query_data_rate(_rate);
}

/**
 * Default destructor
 */
IMU1750::~IMU1750()
{
}

/**
 * Sets temperature limit over which over temp is reported
 */
void IMU1750::set_temp_limit(int max_temp)
{
  _max_temp = max_temp;
}

/**
 * Public read interface, which attempts to read a new KVH message
 * \param[in] msg Storage location for new message.
 * \param[out] size_t Flag indicating read success.
 */
typename IMU1750::ParseResults IMU1750::read(kvh::Message& msg)
{
  if(!base_read())
  {
    return BAD_READ;
  }
  std::vector<char>::iterator match = _buff.end();
  bool header_start = find_header(true, match);

  IMU1750::ParseResults result = VALID;

  if(header_start && _bytes_read == sizeof(kvh::RawMessage))
  {
    //get timestamp from reading
    uint64_t secs = 0;
    uint64_t nsecs = 0;
    _io->time(secs, nsecs);
    kvh::RawMessage* raw = reinterpret_cast<kvh::RawMessage*>(&_buff.front());
    result = (msg.from_raw(*raw, secs, nsecs, _is_c, _is_da) ? VALID : BAD_CRC);
    //cleanup from successful read
    reset_buffer();

    bool celsius;
    int temp = msg.temp(celsius);
    if(!celsius)
    {
      kvh::to_c(temp);
    }
    if(temp >= _max_temp)
    {
      result = OVER_TEMP;
    }
  }
  else
  {
    if(match != _buff.begin())
    {
      reset_partial_buffer(match);
    }
    result = PARTIAL_READ;
  }

  return result;
}

/**
 * Reports last read IMU read rate for general use. This does not
 * stop data from the IMU.
 * \param[out] Rate in Hz
 */
int IMU1750::imu_rate() const
{
  return _rate;
}

/**
 * Reports last read temperature units from IMU. This does not stop
 * data from the IMU.
 * \param[out] Flag indicating if temperatures are in Celsius
 */
bool IMU1750::is_celsius() const
{
  return _is_c;
}

/**
 * Queries the IMU to determine the temperature units.
 * \param[in,out] is_celsius Flag set to indicate if temperature is in C
 * \param[out] Flag indicating if the query came back valid
 */
bool IMU1750::query_temp_units(bool& celsius)
{
  std::string cmd = build_command(TUCmd, "", true);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(TUCmd, match) && parse_temp_units(match);
    if(status)
    {
      celsius = _is_c;
    }
  }

  set_mode(false);
  return status;
}

/**
 * Queries IMU to determine the rotational units.
 * \param[in,out] Flag indicating if angles are raw (delta angles) or filtered
 * to be an angular rate
 * \param[out] Flag indicating if query was processed.
 */
bool IMU1750::query_angle_units(bool& is_da)
{
  std::string cmd = build_command(RFCmd, "", true);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(RFCmd, match);
    if(status)
    {
      status = parse_angle_units(match);
    }
  }

  set_mode(false);
  return status;
}

/**
 * Query IMU for current reporting data rate.
 * \param[in,out] rate_hz Reporting rate in Hz.
 * \param[out] Flag indicating if query succeeded.
 */
bool IMU1750::query_data_rate(int& rate_hz)
{
  std::string cmd = build_command(DRCmd, "", true);
  set_mode(true);
  cmd_write(cmd);
  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(DRCmd, match) &&
      parse_data_rate(match, rate_hz);
  }
  set_mode(false);
  return status;
}

/**
 * Set temperature units (C or F)
 * \param[in] Flag indicating if temperature is in C
 * \param[out] Flag indicating if command succeeded
 */
bool IMU1750::set_temp_units(bool celsius)
{
  const std::string val = (celsius ? "C" : "F");
  std::string cmd = build_command(TUCmd, val, false);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(TUCmd, match) && parse_temp_units(match);
  }
  set_mode(false);
  return status;
}

/**
 * Set reporting data rate for IMU.
 * \param[in] Rate in Hz. Must be one of the options in IMU1750::DataRates
 * \param[out] Flag indicating if command succeeded
 */
bool IMU1750::set_data_rate(int rate_hz)
{
  std::vector<int>::const_iterator dr_match = std::find(IMU1750::DataRates.begin(),
    IMU1750::DataRates.end(), rate_hz);
  if(dr_match == IMU1750::DataRates.end())
  {
    return false;
  }

  const std::string val = std::to_string(rate_hz);
  std::string cmd = build_command(DRCmd, val, false);

  set_mode(true);
  cmd_write(cmd);

  std::vector<char>::iterator match;
  bool status = cmd_read() && find_response(DRCmd, match);

  set_mode(false);
  return status;
}

/**
 * Set angle units for IMU.
 * \param[in] Flag indicating if units are raw (delta angles) or not
 * \param[out] Flag indicating if command succeeded.
 */
bool IMU1750::set_angle_units(bool is_da)
{
  const std::string val = (is_da ? "DELTA" : "RATE");
  std::string cmd = build_command(RFCmd, val, false);
  set_mode(true);
  cmd_write(cmd);

  bool status = cmd_read();
  if(status)
  {
    std::vector<char>::iterator match;
    status = find_response(RFCmd, match);
    if(status)
    {
      status = parse_angle_units(match);
    }
  }
  set_mode(false);
  return status;
}

/**
 * Sets the IMU mode to either normal or config.
 * \param[in] Flag indicating if IMU should enter config mode.
 */
void IMU1750::set_mode(bool enter_config)
{
  if(enter_config == _is_config)
  {
    return;
  }

  std::string val = (enter_config ? "1" : "0");
  std::string cmd = build_command(ConfigCmd, val, false);
  cmd_write(cmd);

  //normal mode never responds with confirmation
  if(enter_config)
  {
    reset_buffer();
    cmd_read();
  }

  _is_config = enter_config;
}

/**
 * Parse buffer for angle units.
 * \param[in] Iterator pointing at start of response parameters
 * \param[out] Flag indicating if response was correctly parsed
 */
bool IMU1750::parse_angle_units(std::vector<char>::iterator match)
{
  bool status = false;
  const std::string Delta = "DELTA";
  const std::string Rate = "RATE";
  if(std::strncmp(&*match, &*Delta.begin(), Delta.length()) == 0)
  {
    _is_da = true;
    status = true;
  }
  else if(std::strncmp(&*match, &*Rate.begin(), Rate.length()) == 0)
  {
    _is_da = false;
    status = true;
  }

  return status;
}

/**
 * Parse buffer for temperature units
 * \param[in] Iterator pointing at start of response parameters
 * \param[out] Flag indicating if response was correctly parsed
 */
bool IMU1750::parse_temp_units(std::vector<char>::iterator match)
{
  bool status = true;
  switch(*match)
  {
    case 'C':
      _is_c = true;
      break;
    case 'F':
      _is_c = false;
      break;
    default:
      status = false;
      break;
  }

  return status;
}

/**
 * Parse buffer for data rate
 * \param[in] Iterator pointing at start of response parameters
 * \param[in,out] Rate value parsed
 * \param[out] Flag indicating if response was correctly parsed
 */
bool IMU1750::parse_data_rate(std::vector<char>::iterator match, int& rate)
{
  rate = std::strtol(&*match, NULL, 10);
  return true;
}

/**
 * Internal read function which performs read and maintains internal state.
 * \param[in] use_tov Flag indicating if the timing signal should be used.
 * \param[out] Flag indicating if read was valid.
 */
bool IMU1750::base_read(bool use_tov)
{
  size_t bytes = 0;
  bool status = _io->read(&_buff.front() + _bytes_read, bytes_remaining(), bytes, use_tov);
  if(!status)
  {
    reset_buffer();
  }
  else
  {
    _bytes_read += bytes;
  }
  return status;
}

/**
 * Attempt to read response from command.
 * \param[out] Flag indicating if buffer now contains the response
 */
bool IMU1750::cmd_read()
{
  const size_t MaxReadAttempts = 20;

  bool response = false;
  for(size_t ii = 0; !response && ii < MaxReadAttempts; ++ii)
  {
    base_read(false);

    if(_buff[_bytes_read - 1] == '\n')
    {
      response = true;
    }
    else if(bytes_remaining() == 0)
    {
      reset_buffer();
    }
  }

  return response;
}

/**
 * Write command to IMU.
 */
void IMU1750::cmd_write(const std::string& cmd)
{
  _io->flush_buffers();
  reset_buffer();
  _io->write(cmd.c_str(), cmd.length());
}

/**
 * Function to search the memory buffer for the header sequence.
 * \param[in,out] iterator pointing to location of header
 * \param[out] Flag indicating if header is at the start of the header.
 */
bool IMU1750::find_header(bool is_imu, std::vector<char>::iterator& match)
{
  std::vector<char>::iterator buffer_end = _buff.begin() + _bytes_read;
  HeaderType::const_iterator header_start = (is_imu ? IMUHeader.begin() : BITHeader.begin());
  HeaderType::const_iterator header_end = (is_imu ? IMUHeader.end() : BITHeader.end());
  match = std::search(_buff.begin(), buffer_end, header_start, header_end);

  return match == _buff.begin();
}

/**
 * Clears and resets the memory buffer used to store the raw message being
 * processed.
 */
void IMU1750::reset_buffer()
{
  _buff.clear();
  _buff.resize(_buff.capacity(), 0);
  _bytes_read = 0;
  _io->reset_time();
}

/**
 * Stores a partially read message at the start of the buffer
 * to simplify processing.
 * \param[in] match Iterator pointing to start of header.
 */
void IMU1750::reset_partial_buffer(const std::vector<char>::iterator& match)
{
  std::vector<char>::iterator buffer_end = _buff.begin() + _bytes_read;
  //start at either the match, or the longest missable substring
  std::vector<char>::iterator start = std::min(match, std::max(_buff.begin(),
    buffer_end - (HeaderSize - 1)));
  std::vector<char> new_buffer(start, buffer_end);
  new_buffer.resize(_buff.capacity(), 0);
  _bytes_read = buffer_end - start;
  _buff.swap(new_buffer);
}

/**
 * Number of bytes available for writing in the buffer.
 */
size_t IMU1750::bytes_remaining() const
{
  return _buff.size() - _bytes_read;
}

/**
 * Sets the buffer size in case larger messages need to be read.
 * \param[in] len Length of internal buffer in bytes
 */
void IMU1750::set_buffer_size(size_t len)
{
  std::vector<char>::reverse_iterator start = std::max(_buff.rend(),
    _buff.rbegin() + len);
  std::vector<char> new_buffer(start, _buff.rend()); //copy last len bytes
  new_buffer.resize(len, 0); //make sure size is correct
  _buff.swap(new_buffer);
}

/**
 * Utility function for building an IMU commands in a consistent fashion.
 * \param[in] Command type.
 * \param[in] Command arguments, if any are needed.
 * \param[in] Flag indicating if command is a query or not.
 * \param[out] String containing the fully built command
 */
std::string IMU1750::build_command(const std::string& type, const std::string& val,
  bool is_query)
{
  std::stringstream ss;
  ss << (is_query ? "?" : "=") << type;
  if(!is_query)
  {
    ss << "," << val;
  }
  ss << "\n";
  return ss.str();
}

/**
 * Utility function to attempt to find a valid response in the internal buffer.
 * \param[in] Command type to search for
 * \param[in,out] Iterator pointing at start of response arguments
 * \pram[out] Flag indicating if response was found
 */
bool IMU1750::find_response(const std::string& type,
  std::vector<char>::iterator& match)
{
  std::string FullMatch = type + ",";
  match = std::search(_buff.begin(), _buff.end(),
    FullMatch.begin(), FullMatch.end());

  bool status = (match != _buff.end());
  match += FullMatch.length();
  return status;
}

}<|endoftext|>


<commit_before>/*
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <unordered_map>
#include <memory>
#include <string>
#include "util/sstream.h"
#include "kernel/abstract_type_context.h"
#include "library/kernel_serializer.h"
#include "library/annotation.h"

namespace lean {
static name * g_annotation = nullptr;
static std::string * g_annotation_opcode = nullptr;

name const & get_annotation_name() { return *g_annotation; }
std::string const & get_annotation_opcode() { return *g_annotation_opcode; }

/** \brief We use a macro to mark expressions that denote "let" and "have"-expressions.
    These marks have no real semantic meaning, but are useful for helping Lean's pretty printer.
*/
class annotation_macro_definition_cell : public macro_definition_cell {
    name m_name;
    void check_macro(expr const & m) const {
        if (!is_macro(m) || macro_num_args(m) != 1)
            throw exception(sstream() << "invalid '" << m_name << "' annotation, incorrect number of arguments");
    }
public:
    annotation_macro_definition_cell(name const & n):m_name(n) {}
    name const & get_annotation_kind() const { return m_name; }
    virtual name get_name() const { return get_annotation_name(); }
    virtual format pp(formatter const &) const { return format(m_name); }
    virtual void display(std::ostream & out) const { out << m_name; }
    virtual expr check_type(expr const & m, abstract_type_context & ctx, bool infer_only) const {
        check_macro(m);
        return ctx.check(macro_arg(m, 0), infer_only);
    }
    virtual optional<expr> expand(expr const & m, abstract_type_context &) const {
        check_macro(m);
        return some_expr(macro_arg(m, 0));
    }
    virtual void write(serializer & s) const {
        s.write_string(get_annotation_opcode());
        s << m_name;
    }
    virtual bool operator==(macro_definition_cell const & other) const {
        if (auto other_ptr = dynamic_cast<annotation_macro_definition_cell const *>(&other)) {
            return m_name == other_ptr->m_name;
        } else {
            return false;
        }
    }
    virtual unsigned hash() const {
        return ::lean::hash(m_name.hash(), g_annotation->hash());
    }
};

typedef std::unordered_map<name, macro_definition, name_hash, name_eq> annotation_macros;
static annotation_macros * g_annotation_macros = nullptr;

annotation_macros & get_annotation_macros() { return *g_annotation_macros; }

void register_annotation(name const & n) {
    annotation_macros & ms = get_annotation_macros();
    lean_assert(ms.find(n) == ms.end());
    ms.insert(mk_pair(n, macro_definition(new annotation_macro_definition_cell(n))));
}

expr mk_annotation(name const & kind, expr const & e, tag g) {
    annotation_macros & ms = get_annotation_macros();
    auto it = ms.find(kind);
    if (it != ms.end()) {
        return mk_macro(it->second, 1, &e, g);
    } else {
        throw exception(sstream() << "unknown annotation kind '" << kind << "'");
    }
}

expr mk_annotation(name const & kind, expr const & e) {
    return mk_annotation(kind, e, e.get_tag());
}

bool is_annotation(expr const & e) {
    return is_macro(e) && macro_def(e).get_name() == get_annotation_name();
}

name const & get_annotation_kind(expr const & e) {
    lean_assert(is_annotation(e));
    return static_cast<annotation_macro_definition_cell const*>(macro_def(e).raw())->get_annotation_kind();
}

bool is_annotation(expr const & e, name const & kind) {
    return is_annotation(e) && get_annotation_kind(e) == kind;
}

expr const & get_annotation_arg(expr const & e) {
    lean_assert(is_annotation(e));
    return macro_arg(e, 0);
}

bool is_nested_annotation(expr const & e, name const & kind) {
    expr const * it = &e;
    while (is_annotation(*it)) {
        if (get_annotation_kind(*it) == kind)
            return true;
        it = &get_annotation_arg(*it);
    }
    return false;
}

expr const & get_nested_annotation_arg(expr const & e) {
    expr const * it = &e;
    while (is_annotation(*it))
        it = &get_annotation_arg(*it);
    return *it;
}

expr copy_annotations(expr const & from, expr const & to) {
    buffer<expr> trace;
    expr const * it = &from;
    while (is_annotation(*it)) {
        trace.push_back(*it);
        it = &get_annotation_arg(*it);
    }
    expr r     = to;
    unsigned i = trace.size();
    while (i > 0) {
        --i;
        r = copy_tag(trace[i], mk_annotation(get_annotation_kind(trace[i]), r));
    }
    return r;
}

static name * g_have       = nullptr;
static name * g_show       = nullptr;
static name * g_checkpoint = nullptr;

expr mk_have_annotation(expr const & e) { return mk_annotation(*g_have, e); }
expr mk_show_annotation(expr const & e) { return mk_annotation(*g_show, e); }
expr mk_checkpoint_annotation(expr const & e) { return mk_annotation(*g_checkpoint, e); }
bool is_have_annotation(expr const & e) { return is_annotation(e, *g_have); }
bool is_show_annotation(expr const & e) { return is_annotation(e, *g_show); }
bool is_checkpoint_annotation(expr const & e) { return is_annotation(e, *g_checkpoint); }

void initialize_annotation() {
    g_annotation = new name("annotation");
    g_annotation_opcode = new std::string("Annot");
    g_annotation_macros = new annotation_macros();
    g_have       = new name("have");
    g_show       = new name("show");
    g_checkpoint = new name("checkpoint");

    register_annotation(*g_have);
    register_annotation(*g_show);
    register_annotation(*g_checkpoint);

    register_macro_deserializer(get_annotation_opcode(),
                                [](deserializer & d, unsigned num, expr const * args) {
                                    if (num != 1)
                                        throw corrupted_stream_exception();
                                    name k;
                                    d >> k;
                                    return mk_annotation(k, args[0]);
                                });
}

void finalize_annotation() {
    delete g_show;
    delete g_have;
    delete g_annotation_macros;
    delete g_annotation_opcode;
    delete g_annotation;
}
}
<commit_msg>fix(library/annotation): memory leak<commit_after>/*
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <unordered_map>
#include <memory>
#include <string>
#include "util/sstream.h"
#include "kernel/abstract_type_context.h"
#include "library/kernel_serializer.h"
#include "library/annotation.h"

namespace lean {
static name * g_annotation = nullptr;
static std::string * g_annotation_opcode = nullptr;

name const & get_annotation_name() { return *g_annotation; }
std::string const & get_annotation_opcode() { return *g_annotation_opcode; }

/** \brief We use a macro to mark expressions that denote "let" and "have"-expressions.
    These marks have no real semantic meaning, but are useful for helping Lean's pretty printer.
*/
class annotation_macro_definition_cell : public macro_definition_cell {
    name m_name;
    void check_macro(expr const & m) const {
        if (!is_macro(m) || macro_num_args(m) != 1)
            throw exception(sstream() << "invalid '" << m_name << "' annotation, incorrect number of arguments");
    }
public:
    annotation_macro_definition_cell(name const & n):m_name(n) {}
    name const & get_annotation_kind() const { return m_name; }
    virtual name get_name() const { return get_annotation_name(); }
    virtual format pp(formatter const &) const { return format(m_name); }
    virtual void display(std::ostream & out) const { out << m_name; }
    virtual expr check_type(expr const & m, abstract_type_context & ctx, bool infer_only) const {
        check_macro(m);
        return ctx.check(macro_arg(m, 0), infer_only);
    }
    virtual optional<expr> expand(expr const & m, abstract_type_context &) const {
        check_macro(m);
        return some_expr(macro_arg(m, 0));
    }
    virtual void write(serializer & s) const {
        s.write_string(get_annotation_opcode());
        s << m_name;
    }
    virtual bool operator==(macro_definition_cell const & other) const {
        if (auto other_ptr = dynamic_cast<annotation_macro_definition_cell const *>(&other)) {
            return m_name == other_ptr->m_name;
        } else {
            return false;
        }
    }
    virtual unsigned hash() const {
        return ::lean::hash(m_name.hash(), g_annotation->hash());
    }
};

typedef std::unordered_map<name, macro_definition, name_hash, name_eq> annotation_macros;
static annotation_macros * g_annotation_macros = nullptr;

annotation_macros & get_annotation_macros() { return *g_annotation_macros; }

void register_annotation(name const & n) {
    annotation_macros & ms = get_annotation_macros();
    lean_assert(ms.find(n) == ms.end());
    ms.insert(mk_pair(n, macro_definition(new annotation_macro_definition_cell(n))));
}

expr mk_annotation(name const & kind, expr const & e, tag g) {
    annotation_macros & ms = get_annotation_macros();
    auto it = ms.find(kind);
    if (it != ms.end()) {
        return mk_macro(it->second, 1, &e, g);
    } else {
        throw exception(sstream() << "unknown annotation kind '" << kind << "'");
    }
}

expr mk_annotation(name const & kind, expr const & e) {
    return mk_annotation(kind, e, e.get_tag());
}

bool is_annotation(expr const & e) {
    return is_macro(e) && macro_def(e).get_name() == get_annotation_name();
}

name const & get_annotation_kind(expr const & e) {
    lean_assert(is_annotation(e));
    return static_cast<annotation_macro_definition_cell const*>(macro_def(e).raw())->get_annotation_kind();
}

bool is_annotation(expr const & e, name const & kind) {
    return is_annotation(e) && get_annotation_kind(e) == kind;
}

expr const & get_annotation_arg(expr const & e) {
    lean_assert(is_annotation(e));
    return macro_arg(e, 0);
}

bool is_nested_annotation(expr const & e, name const & kind) {
    expr const * it = &e;
    while (is_annotation(*it)) {
        if (get_annotation_kind(*it) == kind)
            return true;
        it = &get_annotation_arg(*it);
    }
    return false;
}

expr const & get_nested_annotation_arg(expr const & e) {
    expr const * it = &e;
    while (is_annotation(*it))
        it = &get_annotation_arg(*it);
    return *it;
}

expr copy_annotations(expr const & from, expr const & to) {
    buffer<expr> trace;
    expr const * it = &from;
    while (is_annotation(*it)) {
        trace.push_back(*it);
        it = &get_annotation_arg(*it);
    }
    expr r     = to;
    unsigned i = trace.size();
    while (i > 0) {
        --i;
        r = copy_tag(trace[i], mk_annotation(get_annotation_kind(trace[i]), r));
    }
    return r;
}

static name * g_have       = nullptr;
static name * g_show       = nullptr;
static name * g_checkpoint = nullptr;

expr mk_have_annotation(expr const & e) { return mk_annotation(*g_have, e); }
expr mk_show_annotation(expr const & e) { return mk_annotation(*g_show, e); }
expr mk_checkpoint_annotation(expr const & e) { return mk_annotation(*g_checkpoint, e); }
bool is_have_annotation(expr const & e) { return is_annotation(e, *g_have); }
bool is_show_annotation(expr const & e) { return is_annotation(e, *g_show); }
bool is_checkpoint_annotation(expr const & e) { return is_annotation(e, *g_checkpoint); }

void initialize_annotation() {
    g_annotation = new name("annotation");
    g_annotation_opcode = new std::string("Annot");
    g_annotation_macros = new annotation_macros();
    g_have       = new name("have");
    g_show       = new name("show");
    g_checkpoint = new name("checkpoint");

    register_annotation(*g_have);
    register_annotation(*g_show);
    register_annotation(*g_checkpoint);

    register_macro_deserializer(get_annotation_opcode(),
                                [](deserializer & d, unsigned num, expr const * args) {
                                    if (num != 1)
                                        throw corrupted_stream_exception();
                                    name k;
                                    d >> k;
                                    return mk_annotation(k, args[0]);
                                });
}

void finalize_annotation() {
    delete g_checkpoint;
    delete g_show;
    delete g_have;
    delete g_annotation_macros;
    delete g_annotation_opcode;
    delete g_annotation;
}
}
<|endoftext|>


<commit_before>#include "OpenCV.h"

#include "Point.h"
#include "Matrix.h"
#include "CascadeClassifierWrap.h"
#include "VideoCaptureWrap.h"
#include "VideoWriterWrap.h"
#include "Contours.h"
#include "CamShift.h"
#include "HighGUI.h"
#include "FaceRecognizer.h"
#include "Features2d.h"
#include "Constants.h"
#include "Calib3D.h"
#include "ImgProc.h"
#include "Stereo.h"
#include "BackgroundSubtractor.h"
#include "LDAWrap.h"
#include "Histogram.h"

extern "C" void init(Local<Object> target) {
  Nan::HandleScope scope;
  OpenCV::Init(target);

  Point::Init(target);
  Matrix::Init(target);
#ifdef HAVE_OPENCV_OBJDETECT
  CascadeClassifierWrap::Init(target);
#endif
#ifdef HAVE_OPENCV_VIDEOIO
  VideoCaptureWrap::Init(target);
  VideoWriterWrap::Init(target);
#endif
  Contour::Init(target);
#ifdef HAVE_OPENCV_VIDEO
  TrackedObject::Init(target);
#endif
#ifdef HAVE_OPENCV_HIGHGUI
  NamedWindow::Init(target);
#endif
  Constants::Init(target);
#ifdef HAVE_OPENCV_CALIB3D
  Calib3D::Init(target);
#endif
#ifdef HAVE_OPENCV_IMGPROC
  ImgProc::Init(target);
  Histogram::Init(target);
#endif
#if CV_MAJOR_VERSION < 3
  StereoBM::Init(target);
  StereoSGBM::Init(target);
  StereoGC::Init(target);
#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >=4
  #ifdef HAVE_OPENCV_VIDEO
    BackgroundSubtractorWrap::Init(target);
  #endif
  #ifdef HAVE_OPENCV_FEATURES2D
    Features::Init(target);
  #endif
  LDAWrap::Init(target);
#endif
#endif
#ifdef HAVE_OPENCV_FACE
  FaceRecognizerWrap::Init(target);
#endif
};

NODE_MODULE(opencv, init)
<commit_msg>move backgroundsubtrator::init out if HAVE_OPENCV_VIDEO and into new HAVE_BACKGROUNDSUBTRACTOR<commit_after>#include "OpenCV.h"

#include "Point.h"
#include "Matrix.h"
#include "CascadeClassifierWrap.h"
#include "VideoCaptureWrap.h"
#include "VideoWriterWrap.h"
#include "Contours.h"
#include "CamShift.h"
#include "HighGUI.h"
#include "FaceRecognizer.h"
#include "Features2d.h"
#include "Constants.h"
#include "Calib3D.h"
#include "ImgProc.h"
#include "Stereo.h"
#include "BackgroundSubtractor.h"
#include "LDAWrap.h"
#include "Histogram.h"

extern "C" void init(Local<Object> target) {
  Nan::HandleScope scope;
  OpenCV::Init(target);

  Point::Init(target);
  Matrix::Init(target);
#ifdef HAVE_OPENCV_OBJDETECT
  CascadeClassifierWrap::Init(target);
#endif
#ifdef HAVE_OPENCV_VIDEOIO
  VideoCaptureWrap::Init(target);
  VideoWriterWrap::Init(target);
#endif
  Contour::Init(target);
#ifdef HAVE_OPENCV_VIDEO
  TrackedObject::Init(target);
#endif
#ifdef HAVE_OPENCV_HIGHGUI
  NamedWindow::Init(target);
#endif
  Constants::Init(target);
#ifdef HAVE_OPENCV_CALIB3D
  Calib3D::Init(target);
#endif
#ifdef HAVE_OPENCV_IMGPROC
  ImgProc::Init(target);
  Histogram::Init(target);
#endif
#if CV_MAJOR_VERSION < 3
  StereoBM::Init(target);
  StereoSGBM::Init(target);
  StereoGC::Init(target);

#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >=4
  #ifdef HAVE_OPENCV_FEATURES2D
    Features::Init(target);
  #endif
  LDAWrap::Init(target);
#endif
#endif
#ifdef HAVE_BACKGROUNDSUBTRACTOR
  BackgroundSubtractorWrap::Init(target);
#endif
#ifdef HAVE_OPENCV_FACE
  FaceRecognizerWrap::Init(target);
#endif
};

NODE_MODULE(opencv, init)
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file allocator.hxx
 *
 * Class for maintaining a bounded list of structures that can be allocated.
 *
 * @author Balazs Racz
 * @date 19 October 2013
 */

#ifndef _EXECUTOR_ALLOCATOR_HXX_
#define _EXECUTOR_ALLOCATOR_HXX_

#include "executor/executor.hxx"
#include "executor/notifiable.hxx"

class AllocationResult : public Executable {
 public:
  // Callback from the allocator when an item was successfully allocated.
  virtual void AllocationCallback(QueueMember* entry) = 0;
};

class AllocatorBase : public Lockable {
 public:
  AllocatorBase();

  //! Returns an entry to the pool of free entries. no type checking is
  //! performed by this function.
  //
  //! @param entry is the entry to release. It should be compatible with the
  //! entries expected from this Allocator.
  void Release(QueueMember* entry);

  //! Returns an entry to the end (back) of the pool of free entries. no type
  //! checking is performed by this function.
  //
  //! @param entry is the entry to release. It should be compatible with the
  //! entries expected from this Allocator.
  void ReleaseBack(QueueMember* entry);

  //! Allocates an object, possibly by putting the caller into a waiting list
  //! for allocated objects.
  //
  //! @param caller is a callback. When the next object to allocate is
  //! available, the allocation callback will be called.
  //
  //! The callback may be called inline, but it may also be called from a
  //! different thread. No locking is performed while calling the callback.
  void AllocateEntry(AllocationResult* caller);

  //! @returns true if there is an entry waiting to be allocated.
  //
  //! NOTE: using this method is likely result in race conditions.
  bool Peek();

  //! Synchronously tries to allocate an entry.
  //
  //! @returns the first allocatable entry, if there is any free. Otherwise
  //! returns NULL.
  QueueMember* AllocateOrNull();

 private:
  // List of users waiting for entries OR list of entries available to be
  // allocated.
  Queue waiting_list_;
  // This is true if there are free entries on the waiting_list or the list is
  // empty.
  bool has_free_entries_;
};

//! An allocator class that can be used for type-safe operations.
template <class T>
class TypedAllocator : public AllocatorBase {
 public:
  //! Returns an entry to the allocator.
  //
  //! @param entry is the object to return.
  void TypedRelease(T* entry) { Release(entry); }

  //! Returns an entry to the allocator at the back of the freelist..
  //
  //! @param entry is the object to return.
  void TypedReleaseBack(T* entry) { ReleaseBack(entry); }

  T* TypedAllocateOrNull() { return static_cast<T*>(AllocateOrNull()); }
};

//! Helper class that encapsulates a call to an allocator, which blocks the
//! current thread until a free entry shows up.
//
//! Usage:
//
//! SyncAllocation a(allocator);  // may block the thread
//! DoSomething(a.untyped_result())
class SyncAllocation : private AllocationResult {
 public:
  //! Synchronously allocates an entry from an allocator. Blocks the current
  //! thread until an entry is available.
  //
  //! @param allocator is the allocator to allocate an entry from.
  SyncAllocation(AllocatorBase* allocator) : result_(nullptr) {
    allocator->AllocateEntry(this);
    notify_.WaitForNotification();
  }

  //! @returns the entry allocated.
  QueueMember* untyped_result() { return result_; }

 private:
  //! Callback from the allocator.
  //
  //! @param entry is the entry allocated to this request.
  virtual void AllocationCallback(QueueMember* entry) {
    result_ = entry;
    notify_.Notify();
  }

  //! A callback that should never be called, since *this is never used as an
  //! Executable.
  virtual void Run() {
    extern int unexpected_call_to_syncallocation_run();
    HASSERT(false && unexpected_call_to_syncallocation_run());
  }

 protected:
  //! Stores the result of the allocation callback.
  QueueMember* result_;
  //! Helper for blocking the current thread until the allocation is
  //! successful.
  SyncNotifiable notify_;
};

//! Helper class for type-safe synchronous allocation from an Allocator.
template <class T>
class TypedSyncAllocation : public SyncAllocation {
 public:
  //! Allocates an entry from a (typed) allocator.
  //
  //! @param allocator is the allocator to allocate from.
  TypedSyncAllocation(TypedAllocator<T>* allocator)
      : SyncAllocation(allocator) {}

  //! @returns the result of the allocation.
  T* result() { return static_cast<T*>(untyped_result()); }
};

//! Helper class that simulates a (non-reentrant) mutex using the Allocator
//! queue and a single QueueMember token.
//
//! The mutex is defined as unlocked if there is an entry on the allocator
//! queue. Locking the mutex will take the entry off of the allocator
//! queue. Any other acquisition attempts will block so long as the allocator's
//! queue is empty.
//
//! Unlocking the mutex will release the token back to the allocator, waking up
//! the first caller in the queue.
//
//! To lock the mutex, use any allocation mechanism (e.g. control
//! flow::Allocate, or SyncAllocator). To Unlock the mutex, call the Unlock
//! method.
class AllocatorMutex : public AllocatorBase {
 public:
  //! Creates an allocator mutex.
  AllocatorMutex() { Unlock(); }

  //! Crashes if the the particular value is not the token associated with this
  //! mutex.
  //
  //! @param token is the value to check.
  void CheckToken(QueueMember* token) { HASSERT(token == &token_); }

  //! Crashes if the mutex is not locked.
  void AssertLocked() { HASSERT(!Peek()); }

  //! Crashes if the mutex is locked.
  void AssertUnlocked() { HASSERT(Peek()); }

  //! Unlocks the mutex. Crashes if the mutex is unlocked.
  void Unlock() { Release(&token_); }

  //! Synchronously locks the mutex. Might block the current thread.
  void Lock() { SyncAllocation a(this); }

 private:
  QueueMember token_;
};

#endif  // _EXECUTOR_ALLOCATOR_HXX_
<commit_msg>Adds the ability to a TypedAllocator to cast the result to the appropriate type. This way even callback-based allocation can be handled type-safe.<commit_after>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file allocator.hxx
 *
 * Class for maintaining a bounded list of structures that can be allocated.
 *
 * @author Balazs Racz
 * @date 19 October 2013
 */

#ifndef _EXECUTOR_ALLOCATOR_HXX_
#define _EXECUTOR_ALLOCATOR_HXX_

#include "executor/executor.hxx"
#include "executor/notifiable.hxx"

class AllocationResult : public Executable {
 public:
  // Callback from the allocator when an item was successfully allocated.
  virtual void AllocationCallback(QueueMember* entry) = 0;
};

class AllocatorBase : public Lockable {
 public:
  AllocatorBase();

  //! Returns an entry to the pool of free entries. no type checking is
  //! performed by this function.
  //
  //! @param entry is the entry to release. It should be compatible with the
  //! entries expected from this Allocator.
  void Release(QueueMember* entry);

  //! Returns an entry to the end (back) of the pool of free entries. no type
  //! checking is performed by this function.
  //
  //! @param entry is the entry to release. It should be compatible with the
  //! entries expected from this Allocator.
  void ReleaseBack(QueueMember* entry);

  //! Allocates an object, possibly by putting the caller into a waiting list
  //! for allocated objects.
  //
  //! @param caller is a callback. When the next object to allocate is
  //! available, the allocation callback will be called.
  //
  //! The callback may be called inline, but it may also be called from a
  //! different thread. No locking is performed while calling the callback.
  void AllocateEntry(AllocationResult* caller);

  //! @returns true if there is an entry waiting to be allocated.
  //
  //! NOTE: using this method is likely result in race conditions.
  bool Peek();

  //! Synchronously tries to allocate an entry.
  //
  //! @returns the first allocatable entry, if there is any free. Otherwise
  //! returns NULL.
  QueueMember* AllocateOrNull();

 private:
  // List of users waiting for entries OR list of entries available to be
  // allocated.
  Queue waiting_list_;
  // This is true if there are free entries on the waiting_list or the list is
  // empty.
  bool has_free_entries_;
};

//! An allocator class that can be used for type-safe operations.
template <class T>
class TypedAllocator : public AllocatorBase {
 public:
  //! Returns an entry to the allocator.
  //
  //! @param entry is the object to return.
  void TypedRelease(T* entry) { Release(entry); }

  //! Returns an entry to the allocator at the back of the freelist..
  //
  //! @param entry is the object to return.
  void TypedReleaseBack(T* entry) { ReleaseBack(entry); }

  T* TypedAllocateOrNull() { return cast_result(AllocateOrNull()); }

  static T* cast_result(QueueMember* entry) { return static_cast<T*>(entry); }
};

//! Helper class that encapsulates a call to an allocator, which blocks the
//! current thread until a free entry shows up.
//
//! Usage:
//
//! SyncAllocation a(allocator);  // may block the thread
//! DoSomething(a.untyped_result())
class SyncAllocation : private AllocationResult {
 public:
  //! Synchronously allocates an entry from an allocator. Blocks the current
  //! thread until an entry is available.
  //
  //! @param allocator is the allocator to allocate an entry from.
  SyncAllocation(AllocatorBase* allocator) : result_(nullptr) {
    allocator->AllocateEntry(this);
    notify_.WaitForNotification();
  }

  //! @returns the entry allocated.
  QueueMember* untyped_result() { return result_; }

 private:
  //! Callback from the allocator.
  //
  //! @param entry is the entry allocated to this request.
  virtual void AllocationCallback(QueueMember* entry) {
    result_ = entry;
    notify_.Notify();
  }

  //! A callback that should never be called, since *this is never used as an
  //! Executable.
  virtual void Run() {
    extern int unexpected_call_to_syncallocation_run();
    HASSERT(false && unexpected_call_to_syncallocation_run());
  }

 protected:
  //! Stores the result of the allocation callback.
  QueueMember* result_;
  //! Helper for blocking the current thread until the allocation is
  //! successful.
  SyncNotifiable notify_;
};

//! Helper class for type-safe synchronous allocation from an Allocator.
template <class T>
class TypedSyncAllocation : public SyncAllocation {
 public:
  //! Allocates an entry from a (typed) allocator.
  //
  //! @param allocator is the allocator to allocate from.
  TypedSyncAllocation(TypedAllocator<T>* allocator)
      : SyncAllocation(allocator) {}

  //! @returns the result of the allocation.
  T* result() { return static_cast<T*>(untyped_result()); }
};

//! Helper class that simulates a (non-reentrant) mutex using the Allocator
//! queue and a single QueueMember token.
//
//! The mutex is defined as unlocked if there is an entry on the allocator
//! queue. Locking the mutex will take the entry off of the allocator
//! queue. Any other acquisition attempts will block so long as the allocator's
//! queue is empty.
//
//! Unlocking the mutex will release the token back to the allocator, waking up
//! the first caller in the queue.
//
//! To lock the mutex, use any allocation mechanism (e.g. control
//! flow::Allocate, or SyncAllocator). To Unlock the mutex, call the Unlock
//! method.
class AllocatorMutex : public AllocatorBase {
 public:
  //! Creates an allocator mutex.
  AllocatorMutex() { Unlock(); }

  //! Crashes if the the particular value is not the token associated with this
  //! mutex.
  //
  //! @param token is the value to check.
  void CheckToken(QueueMember* token) { HASSERT(token == &token_); }

  //! Crashes if the mutex is not locked.
  void AssertLocked() { HASSERT(!Peek()); }

  //! Crashes if the mutex is locked.
  void AssertUnlocked() { HASSERT(Peek()); }

  //! Unlocks the mutex. Crashes if the mutex is unlocked.
  void Unlock() { Release(&token_); }

  //! Synchronously locks the mutex. Might block the current thread.
  void Lock() { SyncAllocation a(this); }

 private:
  QueueMember token_;
};

#endif  // _EXECUTOR_ALLOCATOR_HXX_
<|endoftext|>


<commit_before>#ifndef slic3r_SLAPrint_hpp_
#define slic3r_SLAPrint_hpp_

#include <mutex>

#include "PrintBase.hpp"
#include "PrintExport.hpp"
#include "Point.hpp"
#include "MTUtils.hpp"

namespace Slic3r {

enum SLAPrintStep : unsigned int {
	slapsRasterize,
	slapsValidate,
	slapsCount
};

enum SLAPrintObjectStep : unsigned int {
	slaposObjectSlice,
	slaposSupportIslands,
	slaposSupportPoints,
	slaposSupportTree,
	slaposBasePool,
	slaposSliceSupports,
	slaposCount
};

class SLAPrint;
class GLCanvas;

using _SLAPrintObjectBase =
    PrintObjectBaseWithState<SLAPrint, SLAPrintObjectStep, slaposCount>;

class SLAPrintObject : public _SLAPrintObjectBase
{
private: // Prevents erroneous use by other classes.
    using Inherited = _SLAPrintObjectBase;

public:
    const SLAPrintObjectConfig& config() const { return m_config; }
    const Transform3d&          trafo()  const { return m_trafo; }

    struct Instance {
    	Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
		bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
    	// ID of the corresponding ModelInstance.
		ModelID instance_id;
		// Slic3r::Point objects in scaled G-code coordinates
    	Point 	shift;
    	// Rotation along the Z axis, in radians.
    	float 	rotation;
	};
    const std::vector<Instance>& instances() const { return m_instances; }

    bool                    has_mesh(SLAPrintObjectStep step) const;
    TriangleMesh            get_mesh(SLAPrintObjectStep step) const;

    // Get a support mesh centered around origin in XY, and with zero rotation around Z applied.
    // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true.
    const TriangleMesh&     support_mesh() const;
    // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied.
    // Support mesh is only valid if this->is_step_done(slaposPad) is true.
    const TriangleMesh&     pad_mesh() const;

    // This will return the transformed mesh which is cached
    const TriangleMesh&     transformed_mesh() const;

    std::vector<Vec3d>      transformed_support_points() const;

    // Get the needed Z elevation for the model geometry if supports should be
    // displayed. This Z offset should also be applied to the support
    // geometries. Note that this is not the same as the value stored in config
    // as the pad height also needs to be considered.
    double get_elevation() const;

    // Should be obvious
    const std::vector<ExPolygons>& get_support_slices() const;
    const std::vector<ExPolygons>& get_model_slices() const;

    // I refuse to grantee copying (Tamas)
    SLAPrintObject(const SLAPrintObject&) = delete;
    SLAPrintObject& operator=(const SLAPrintObject&) = delete;

protected:
    // to be called from SLAPrint only.
    friend class SLAPrint;

	SLAPrintObject(SLAPrint* print, ModelObject* model_object);
    ~SLAPrintObject();

    void                    config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
    void                    config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
    	{ this->m_config.apply_only(other, keys, ignore_nonexistent); }

    void                    set_trafo(const Transform3d& trafo) {
        m_transformed_rmesh.invalidate([this, &trafo](){ m_trafo = trafo; });
    }

    void                    set_instances(const std::vector<Instance> &instances) { m_instances = instances; }
    // Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint.
    bool                    invalidate_step(SLAPrintObjectStep step);
    bool                    invalidate_all_steps();
    // Invalidate steps based on a set of parameters changed.
    bool                    invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);

private:
    // Object specific configuration, pulled from the configuration layer.
    SLAPrintObjectConfig                    m_config;
    // Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
    Transform3d                             m_trafo = Transform3d::Identity();
    std::vector<Instance> 					m_instances;

    // Which steps have to be performed. Implicitly: all
    std::vector<bool>                       m_stepmask;
    std::vector<ExPolygons>                 m_model_slices;

    // Caching the transformed (m_trafo) raw mesh of the object
    mutable CachedObject<TriangleMesh>      m_transformed_rmesh;

    class SupportData;
    std::unique_ptr<SupportData> m_supportdata;
};

using PrintObjects = std::vector<SLAPrintObject*>;

class TriangleMesh;

/**
 * @brief This class is the high level FSM for the SLA printing process.
 *
 * It should support the background processing framework and contain the
 * metadata for the support geometries and their slicing. It should also
 * dispatch the SLA printing configuration values to the appropriate calculation
 * steps.
 */
class SLAPrint : public PrintBaseWithState<SLAPrintStep, slapsCount>
{
private: // Prevents erroneous use by other classes.
    typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;

public:
    SLAPrint(): m_stepmask(slapsCount, true) {}

	virtual ~SLAPrint() { this->clear(); }

	PrinterTechnology	technology() const noexcept { return ptSLA; }

    void                clear() override;
    bool                empty() const override { return false; }
    ApplyStatus         apply(const Model &model, const DynamicPrintConfig &config) override;
    void                process() override;

    template<class Fmt> void export_raster(const std::string& fname) {
        if(m_printer) m_printer->save<Fmt>(fname);
    }
    const PrintObjects& objects() const { return m_objects; }

private:
    using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
    using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;

    // Invalidate steps based on a set of parameters changed.
    bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);

    SLAPrinterConfig                m_printer_config;
    SLAMaterialConfig               m_material_config;
    SLAPrintObjectConfig            m_default_object_config;

    PrintObjects                    m_objects;
    std::vector<bool>               m_stepmask;
    SLAPrinterPtr                   m_printer;

	friend SLAPrintObject;
};

} // namespace Slic3r

#endif /* slic3r_SLAPrint_hpp_ */
<commit_msg>Fixed a crash on SLA with background processing on and with an empty scene.<commit_after>#ifndef slic3r_SLAPrint_hpp_
#define slic3r_SLAPrint_hpp_

#include <mutex>

#include "PrintBase.hpp"
#include "PrintExport.hpp"
#include "Point.hpp"
#include "MTUtils.hpp"

namespace Slic3r {

enum SLAPrintStep : unsigned int {
	slapsRasterize,
	slapsValidate,
	slapsCount
};

enum SLAPrintObjectStep : unsigned int {
	slaposObjectSlice,
	slaposSupportIslands,
	slaposSupportPoints,
	slaposSupportTree,
	slaposBasePool,
	slaposSliceSupports,
	slaposCount
};

class SLAPrint;
class GLCanvas;

using _SLAPrintObjectBase =
    PrintObjectBaseWithState<SLAPrint, SLAPrintObjectStep, slaposCount>;

class SLAPrintObject : public _SLAPrintObjectBase
{
private: // Prevents erroneous use by other classes.
    using Inherited = _SLAPrintObjectBase;

public:
    const SLAPrintObjectConfig& config() const { return m_config; }
    const Transform3d&          trafo()  const { return m_trafo; }

    struct Instance {
    	Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
		bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
    	// ID of the corresponding ModelInstance.
		ModelID instance_id;
		// Slic3r::Point objects in scaled G-code coordinates
    	Point 	shift;
    	// Rotation along the Z axis, in radians.
    	float 	rotation;
	};
    const std::vector<Instance>& instances() const { return m_instances; }

    bool                    has_mesh(SLAPrintObjectStep step) const;
    TriangleMesh            get_mesh(SLAPrintObjectStep step) const;

    // Get a support mesh centered around origin in XY, and with zero rotation around Z applied.
    // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true.
    const TriangleMesh&     support_mesh() const;
    // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied.
    // Support mesh is only valid if this->is_step_done(slaposPad) is true.
    const TriangleMesh&     pad_mesh() const;

    // This will return the transformed mesh which is cached
    const TriangleMesh&     transformed_mesh() const;

    std::vector<Vec3d>      transformed_support_points() const;

    // Get the needed Z elevation for the model geometry if supports should be
    // displayed. This Z offset should also be applied to the support
    // geometries. Note that this is not the same as the value stored in config
    // as the pad height also needs to be considered.
    double get_elevation() const;

    // Should be obvious
    const std::vector<ExPolygons>& get_support_slices() const;
    const std::vector<ExPolygons>& get_model_slices() const;

    // I refuse to grantee copying (Tamas)
    SLAPrintObject(const SLAPrintObject&) = delete;
    SLAPrintObject& operator=(const SLAPrintObject&) = delete;

protected:
    // to be called from SLAPrint only.
    friend class SLAPrint;

	SLAPrintObject(SLAPrint* print, ModelObject* model_object);
    ~SLAPrintObject();

    void                    config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
    void                    config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
    	{ this->m_config.apply_only(other, keys, ignore_nonexistent); }

    void                    set_trafo(const Transform3d& trafo) {
        m_transformed_rmesh.invalidate([this, &trafo](){ m_trafo = trafo; });
    }

    void                    set_instances(const std::vector<Instance> &instances) { m_instances = instances; }
    // Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint.
    bool                    invalidate_step(SLAPrintObjectStep step);
    bool                    invalidate_all_steps();
    // Invalidate steps based on a set of parameters changed.
    bool                    invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);

private:
    // Object specific configuration, pulled from the configuration layer.
    SLAPrintObjectConfig                    m_config;
    // Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
    Transform3d                             m_trafo = Transform3d::Identity();
    std::vector<Instance> 					m_instances;

    // Which steps have to be performed. Implicitly: all
    std::vector<bool>                       m_stepmask;
    std::vector<ExPolygons>                 m_model_slices;

    // Caching the transformed (m_trafo) raw mesh of the object
    mutable CachedObject<TriangleMesh>      m_transformed_rmesh;

    class SupportData;
    std::unique_ptr<SupportData> m_supportdata;
};

using PrintObjects = std::vector<SLAPrintObject*>;

class TriangleMesh;

/**
 * @brief This class is the high level FSM for the SLA printing process.
 *
 * It should support the background processing framework and contain the
 * metadata for the support geometries and their slicing. It should also
 * dispatch the SLA printing configuration values to the appropriate calculation
 * steps.
 */
class SLAPrint : public PrintBaseWithState<SLAPrintStep, slapsCount>
{
private: // Prevents erroneous use by other classes.
    typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;

public:
    SLAPrint(): m_stepmask(slapsCount, true) {}

	virtual ~SLAPrint() { this->clear(); }

	PrinterTechnology	technology() const noexcept { return ptSLA; }

    void                clear() override;
    bool                empty() const override { return m_objects.empty(); }
    ApplyStatus         apply(const Model &model, const DynamicPrintConfig &config) override;
    void                process() override;

    template<class Fmt> void export_raster(const std::string& fname) {
        if(m_printer) m_printer->save<Fmt>(fname);
    }
    const PrintObjects& objects() const { return m_objects; }

private:
    using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
    using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;

    // Invalidate steps based on a set of parameters changed.
    bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);

    SLAPrinterConfig                m_printer_config;
    SLAMaterialConfig               m_material_config;
    SLAPrintObjectConfig            m_default_object_config;

    PrintObjects                    m_objects;
    std::vector<bool>               m_stepmask;
    SLAPrinterPtr                   m_printer;

	friend SLAPrintObject;
};

} // namespace Slic3r

#endif /* slic3r_SLAPrint_hpp_ */
<|endoftext|>