text: //---------------------------------------------------------------------------------
  //
  //  Little Color Management System
  //  Copyright (c) 1998-2020 Marti Maria Saguer
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the "Software"),
  // to deal in the Software without restriction, including without limitation
  // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  // and/or sell copies of the Software, and to permit persons to whom the Software
  // is furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  //---------------------------------------------------------------------------------
  //
  
  #include "lcms2_internal.h"
  
  
  // ----------------------------------------------------------------------------------
  // Encoding & Decoding support functions
  // ----------------------------------------------------------------------------------
  
  //      Little-Endian to Big-Endian
  
  // Adjust a word value after being read/ before being written from/to an ICC profile
  cmsUInt16Number CMSEXPORT  _cmsAdjustEndianess16(cmsUInt16Number Word)
  {
  #ifndef CMS_USE_BIG_ENDIAN
  
      cmsUInt8Number* pByte = (cmsUInt8Number*) &Word;
      cmsUInt8Number tmp;
  
      tmp = pByte[0];
      pByte[0] = pByte[1];
      pByte[1] = tmp;
  #endif
  
      return Word;
  }
  
  
  // Transports to properly encoded values - note that icc profiles does use big endian notation.
  
  // 1 2 3 4
  // 4 3 2 1
  
  cmsUInt32Number CMSEXPORT  _cmsAdjustEndianess32(cmsUInt32Number DWord)
  {
  #ifndef CMS_USE_BIG_ENDIAN
  
      cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
      cmsUInt8Number temp1;
      cmsUInt8Number temp2;
  
      temp1 = *pByte++;
      temp2 = *pByte++;
      *(pByte-1) = *pByte;
      *pByte++ = temp2;
      *(pByte-3) = *pByte;
      *pByte = temp1;
  #endif
      return DWord;
  }
  
  // 1 2 3 4 5 6 7 8
  // 8 7 6 5 4 3 2 1
  
  void CMSEXPORT  _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord)
  {
  
  #ifndef CMS_USE_BIG_ENDIAN
  
      cmsUInt8Number* pIn  = (cmsUInt8Number*) QWord;
      cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
  
      _cmsAssert(Result != NULL);
  
      pOut[7] = pIn[0];
      pOut[6] = pIn[1];
      pOut[5] = pIn[2];
      pOut[4] = pIn[3];
      pOut[3] = pIn[4];
      pOut[2] = pIn[5];
      pOut[1] = pIn[6];
      pOut[0] = pIn[7];
  
  #else
      _cmsAssert(Result != NULL);
  
  #  ifdef CMS_DONT_USE_INT64
      (*Result)[0] = (*QWord)[0];
      (*Result)[1] = (*QWord)[1];
  #  else
      *Result = *QWord;
  #  endif
  #endif
  }
  
  // Auxiliary -- read 8, 16 and 32-bit numbers
  cmsBool CMSEXPORT  _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n)
  {
      cmsUInt8Number tmp;
  
      _cmsAssert(io != NULL);
  
      if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
              return FALSE;
  
      if (n != NULL) *n = tmp;
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n)
  {
      cmsUInt16Number tmp;
  
      _cmsAssert(io != NULL);
  
      if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
              return FALSE;
  
      if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
  {
      cmsUInt32Number i;
  
      _cmsAssert(io != NULL);
  
      for (i=0; i < n; i++) {
  
          if (Array != NULL) {
              if (!_cmsReadUInt16Number(io, Array + i)) return FALSE;
          }
          else {
              if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
          }
  
      }
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n)
  {
      cmsUInt32Number tmp;
  
      _cmsAssert(io != NULL);
  
      if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
              return FALSE;
  
      if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
  {
      cmsUInt32Number tmp;
  
      _cmsAssert(io != NULL);
  
      if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
          return FALSE;
  
      if (n != NULL) {
  
          tmp = _cmsAdjustEndianess32(tmp);
          *n = *(cmsFloat32Number*)(void*)&tmp;
          
          // Safeguard which covers against absurd values
          if (*n > 1E+20 || *n < -1E+20) return FALSE;
  
          #if defined(_MSC_VER) && _MSC_VER < 1800
             return TRUE;
          #elif defined (__BORLANDC__)
             return TRUE;
          #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
             return TRUE;
          #else
  
             // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
             return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
          #endif        
      }
  
      return TRUE;
  }
  
  
  cmsBool CMSEXPORT   _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
  {
      cmsUInt64Number tmp;
  
      _cmsAssert(io != NULL);
  
      if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
              return FALSE;
  
      if (n != NULL) {
  
          _cmsAdjustEndianess64(n, &tmp);
      }
  
      return TRUE;
  }
  
  
  cmsBool CMSEXPORT  _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
  {
      cmsUInt32Number tmp;
  
      _cmsAssert(io != NULL);
  
      if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
              return FALSE;
  
      if (n != NULL) {
          *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
      }
  
      return TRUE;
  }
  
  
  cmsBool CMSEXPORT  _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
  {
      cmsEncodedXYZNumber xyz;
  
      _cmsAssert(io != NULL);
  
      if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
  
      if (XYZ != NULL) {
  
          XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
          XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
          XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
      }
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
  {
      _cmsAssert(io != NULL);
  
      if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
              return FALSE;
  
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
  {
      cmsUInt16Number tmp;
  
      _cmsAssert(io != NULL);
  
      tmp = _cmsAdjustEndianess16(n);
      if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
              return FALSE;
  
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
  {
      cmsUInt32Number i;
  
      _cmsAssert(io != NULL);
      _cmsAssert(Array != NULL);
  
      for (i=0; i < n; i++) {
          if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
      }
  
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
  {
      cmsUInt32Number tmp;
  
      _cmsAssert(io != NULL);
  
      tmp = _cmsAdjustEndianess32(n);
      if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
              return FALSE;
  
      return TRUE;
  }
  
  
  cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
  {
      cmsUInt32Number tmp;
  
      _cmsAssert(io != NULL);
  
      tmp = *(cmsUInt32Number*) (void*) &n;
      tmp = _cmsAdjustEndianess32(tmp);
      if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
              return FALSE;
  
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
  {
      cmsUInt64Number tmp;
  
      _cmsAssert(io != NULL);
  
      _cmsAdjustEndianess64(&tmp, n);
      if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
              return FALSE;
  
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
  {
      cmsUInt32Number tmp;
  
      _cmsAssert(io != NULL);
  
      tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
      if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
              return FALSE;
  
      return TRUE;
  }
  
  cmsBool CMSEXPORT  _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
  {
      cmsEncodedXYZNumber xyz;
  
      _cmsAssert(io != NULL);
      _cmsAssert(XYZ != NULL);
  
      xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
      xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
      xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
  
      return io -> Write(io,  sizeof(cmsEncodedXYZNumber), &xyz);
  }
  
  // from Fixed point 8.8 to double
  cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
  {
         cmsUInt8Number  msb, lsb;
  
         lsb = (cmsUInt8Number) (fixed8 & 0xff);
         msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
  
         return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
  }
  
  cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
  {
      cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
      return  (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
  }
  
  // from Fixed point 15.16 to double
  cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
  {
      cmsFloat64Number floater, sign, mid;
      int Whole, FracPart;
  
      sign  = (fix32 < 0 ? -1 : 1);
      fix32 = abs(fix32);
  
      Whole     = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
      FracPart  = (cmsUInt16Number)(fix32 & 0xffff);
  
      mid     = (cmsFloat64Number) FracPart / 65536.0;
      floater = (cmsFloat64Number) Whole + mid;
  
      return sign * floater;
  }
  
  // from double to Fixed point 15.16
  cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
  {
      return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
  }
  
  // Date/Time functions
  
  void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
  {
  
      _cmsAssert(Dest != NULL);
      _cmsAssert(Source != NULL);
  
      Dest->tm_sec   = _cmsAdjustEndianess16(Source->seconds);
      Dest->tm_min   = _cmsAdjustEndianess16(Source->minutes);
      Dest->tm_hour  = _cmsAdjustEndianess16(Source->hours);
      Dest->tm_mday  = _cmsAdjustEndianess16(Source->day);
      Dest->tm_mon   = _cmsAdjustEndianess16(Source->month) - 1;
      Dest->tm_year  = _cmsAdjustEndianess16(Source->year) - 1900;
      Dest->tm_wday  = -1;
      Dest->tm_yday  = -1;
      Dest->tm_isdst = 0;
  }
  
  void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
  {
      _cmsAssert(Dest != NULL);
      _cmsAssert(Source != NULL);
  
      Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
      Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
      Dest->hours   = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
      Dest->day     = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
      Dest->month   = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
      Dest->year    = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
  }
  
  // Read base and return type base
  cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
  {
      _cmsTagBase Base;
  
      _cmsAssert(io != NULL);
  
      if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
          return (cmsTagTypeSignature) 0;
  
      return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
  }
  
  // Setup base marker
  cmsBool  CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
  {
      _cmsTagBase  Base;
  
      _cmsAssert(io != NULL);
  
      Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
      memset(&Base.reserved, 0, sizeof(Base.reserved));
      return io -> Write(io, sizeof(_cmsTagBase), &Base);
  }
  
  cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
  {
      cmsUInt8Number  Buffer[4];
      cmsUInt32Number NextAligned, At;
      cmsUInt32Number BytesToNextAlignedPos;
  
      _cmsAssert(io != NULL);
  
      At = io -> Tell(io);
      NextAligned = _cmsALIGNLONG(At);
      BytesToNextAlignedPos = NextAligned - At;
      if (BytesToNextAlignedPos == 0) return TRUE;
      if (BytesToNextAlignedPos > 4)  return FALSE;
  
      return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
  }
  
  cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
  {
      cmsUInt8Number  Buffer[4];
      cmsUInt32Number NextAligned, At;
      cmsUInt32Number BytesToNextAlignedPos;
  
      _cmsAssert(io != NULL);
  
      At = io -> Tell(io);
      NextAligned = _cmsALIGNLONG(At);
      BytesToNextAlignedPos = NextAligned - At;
      if (BytesToNextAlignedPos == 0) return TRUE;
      if (BytesToNextAlignedPos > 4)  return FALSE;
  
      memset(Buffer, 0, BytesToNextAlignedPos);
      return io -> Write(io, BytesToNextAlignedPos, Buffer);
  }
  
  
  // To deal with text streams. 2K at most
  cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
  {
      va_list args;
      int len;
      cmsUInt8Number Buffer[2048];
      cmsBool rc;
  
      _cmsAssert(io != NULL);
      _cmsAssert(frm != NULL);
  
      va_start(args, frm);
  
      len = vsnprintf((char*) Buffer, 2047, frm, args);
      if (len < 0) {
          va_end(args);
          return FALSE;   // Truncated, which is a fatal error for us
      }
  
      rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
  
      va_end(args);
  
      return rc;
  }
  
  
  // Plugin memory management -------------------------------------------------------------------------------------------------
  
  // Specialized malloc for plug-ins, that is freed upon exit.
  void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
  {
      struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
  
      if (ctx ->MemPool == NULL) {
  
          if (ContextID == NULL) {
  
              ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
              if (ctx->MemPool == NULL) return NULL;
          }
          else {
              cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
              return NULL;
          }
      }
  
      return _cmsSubAlloc(ctx->MemPool, size);
  }
  
  
  // Main plug-in dispatcher
  cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
  {
      return cmsPluginTHR(NULL, Plug_in);
  }
  
  cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
  {
      cmsPluginBase* Plugin;
  
      for (Plugin = (cmsPluginBase*) Plug_in;
           Plugin != NULL;
           Plugin = Plugin -> Next) {
  
              if (Plugin -> Magic != cmsPluginMagicNumber) {
                  cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
                  return FALSE;
              }
  
              if (Plugin ->ExpectedVersion > LCMS_VERSION) {
                  cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
                      Plugin ->ExpectedVersion, LCMS_VERSION);
                  return FALSE;
              }
  
              switch (Plugin -> Type) {
  
                  case cmsPluginMemHandlerSig:
                      if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginInterpolationSig:
                      if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginTagTypeSig:
                      if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginTagSig:
                      if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginFormattersSig:
                      if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginRenderingIntentSig:
                      if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginParametricCurveSig:
                      if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginMultiProcessElementSig:
                      if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginOptimizationSig:
                      if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginTransformSig:
                      if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
                      break;
  
                  case cmsPluginMutexSig:
                      if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
                      break;
  
                  default:
                      cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
                      return FALSE;
              }
      }
  
      // Keep a reference to the plug-in
      return TRUE;
  }
  
  
  // Revert all plug-ins to default
  void CMSEXPORT cmsUnregisterPlugins(void)
  {
      cmsUnregisterPluginsTHR(NULL);
  }
  
  
  // The Global storage for system context. This is the one and only global variable
  // pointers structure. All global vars are referenced here.
  static struct _cmsContext_struct globalContext = {
  
      NULL,                              // Not in the linked list
      NULL,                              // No suballocator
      {
          NULL,                          //  UserPtr,            
          &_cmsLogErrorChunk,            //  Logger,
          &_cmsAlarmCodesChunk,          //  AlarmCodes,
          &_cmsAdaptationStateChunk,     //  AdaptationState, 
          &_cmsMemPluginChunk,           //  MemPlugin,
          &_cmsInterpPluginChunk,        //  InterpPlugin,
          &_cmsCurvesPluginChunk,        //  CurvesPlugin,
          &_cmsFormattersPluginChunk,    //  FormattersPlugin,
          &_cmsTagTypePluginChunk,       //  TagTypePlugin,
          &_cmsTagPluginChunk,           //  TagPlugin,
          &_cmsIntentsPluginChunk,       //  IntentPlugin,
          &_cmsMPETypePluginChunk,       //  MPEPlugin,
          &_cmsOptimizationPluginChunk,  //  OptimizationPlugin,
          &_cmsTransformPluginChunk,     //  TransformPlugin,
          &_cmsMutexPluginChunk          //  MutexPlugin
      },
      
      { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
  };
  
  
  // The context pool (linked list head)
  static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
  static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
  
  // Internal, get associated pointer, with guessing. Never returns NULL.
  struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
  {
      struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
      struct _cmsContext_struct* ctx;
  
  
      // On 0, use global settings
      if (id == NULL) 
          return &globalContext;
  
      // Search
      _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
  
      for (ctx = _cmsContextPoolHead;
           ctx != NULL;
           ctx = ctx ->Next) {
  
              // Found it?
          if (id == ctx)
          {
              _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
              return ctx; // New-style context
          }
      }
  
      _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
      return &globalContext;
  }
  
  
  // Internal: get the memory area associanted with each context client
  // Returns the block assigned to the specific zone. Never return NULL.
  void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
  {
      struct _cmsContext_struct* ctx;
      void *ptr;
  
      if ((int) mc < 0 || mc >= MemoryClientMax) {
          
             cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption");
  
             // This is catastrophic. Should never reach here
             _cmsAssert(0);
  
             // Reverts to global context
             return globalContext.chunks[UserPtr];
      }
      
      ctx = _cmsGetContext(ContextID);
      ptr = ctx ->chunks[mc];
  
      if (ptr != NULL)
          return ptr;
  
      // A null ptr means no special settings for that context, and this 
      // reverts to Context0 globals
      return globalContext.chunks[mc];    
  }
  
  
  // This function returns the given context its default pristine state,
  // as no plug-ins were declared. There is no way to unregister a single 
  // plug-in, as a single call to cmsPluginTHR() function may register 
  // many different plug-ins simultaneously, then there is no way to 
  // identify which plug-in to unregister.
  void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
  {
      _cmsRegisterMemHandlerPlugin(ContextID, NULL);
      _cmsRegisterInterpPlugin(ContextID, NULL);
      _cmsRegisterTagTypePlugin(ContextID, NULL);
      _cmsRegisterTagPlugin(ContextID, NULL);
      _cmsRegisterFormattersPlugin(ContextID, NULL);
      _cmsRegisterRenderingIntentPlugin(ContextID, NULL);
      _cmsRegisterParametricCurvesPlugin(ContextID, NULL);
      _cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
      _cmsRegisterOptimizationPlugin(ContextID, NULL);
      _cmsRegisterTransformPlugin(ContextID, NULL);    
      _cmsRegisterMutexPlugin(ContextID, NULL);
  }
  
  
  // Returns the memory manager plug-in, if any, from the Plug-in bundle
  static
  cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
  {
      cmsPluginBase* Plugin;
  
      for (Plugin = (cmsPluginBase*) PluginBundle;
          Plugin != NULL;
          Plugin = Plugin -> Next) {
  
              if (Plugin -> Magic == cmsPluginMagicNumber && 
                  Plugin -> ExpectedVersion <= LCMS_VERSION && 
                  Plugin -> Type == cmsPluginMemHandlerSig) {
  
                      // Found!
                      return (cmsPluginMemHandler*) Plugin;  
              }
      }
  
      // Nope, revert to defaults 
      return NULL;
  }
  
  
  // Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined 
  // data that will be forwarded to plug-ins and logger.
  cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
  {
      struct _cmsContext_struct* ctx;
      struct _cmsContext_struct  fakeContext;
          
      // See the comments regarding locking in lcms2_internal.h
      // for an explanation of why we need the following code.
  #ifndef CMS_NO_PTHREADS
  #ifdef CMS_IS_WINDOWS_
  #ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
      {
          static HANDLE _cmsWindowsInitMutex = NULL;
          static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
  
          if (*mutex == NULL)
          {
              HANDLE p = CreateMutex(NULL, FALSE, NULL);
              if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL)
                  CloseHandle(p);
          }
          if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
              return NULL;
          if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL)
              InitializeCriticalSection(&_cmsContextPoolHeadMutex);
          if (*mutex == NULL || !ReleaseMutex(*mutex))
              return NULL;
      }
  #endif
  #endif
  #endif
  
      _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
      
      fakeContext.chunks[UserPtr]     = UserData;
      fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
  
      // Create the context structure.
      ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
      if (ctx == NULL)   
          return NULL;     // Something very wrong happened!
  
      // Init the structure and the memory manager
      memset(ctx, 0, sizeof(struct _cmsContext_struct));
  
      // Keep memory manager
      memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk)); 
     
      // Maintain the linked list (with proper locking)
      _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
         ctx ->Next = _cmsContextPoolHead;
         _cmsContextPoolHead = ctx;
      _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
  
      ctx ->chunks[UserPtr]     = UserData;
      ctx ->chunks[MemPlugin]   = &ctx->DefaultMemoryManager;
     
      // Now we can allocate the pool by using default memory manager
      ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 22 pointers
      if (ctx ->MemPool == NULL) {
  
           cmsDeleteContext(ctx);
          return NULL;
      }
  
      _cmsAllocLogErrorChunk(ctx, NULL);
      _cmsAllocAlarmCodesChunk(ctx, NULL);
      _cmsAllocAdaptationStateChunk(ctx, NULL);
      _cmsAllocMemPluginChunk(ctx, NULL);
      _cmsAllocInterpPluginChunk(ctx, NULL);
      _cmsAllocCurvesPluginChunk(ctx, NULL);
      _cmsAllocFormattersPluginChunk(ctx, NULL);
      _cmsAllocTagTypePluginChunk(ctx, NULL);
      _cmsAllocMPETypePluginChunk(ctx, NULL);
      _cmsAllocTagPluginChunk(ctx, NULL);
      _cmsAllocIntentsPluginChunk(ctx, NULL);
      _cmsAllocOptimizationPluginChunk(ctx, NULL);
      _cmsAllocTransformPluginChunk(ctx, NULL);
      _cmsAllocMutexPluginChunk(ctx, NULL);
  
      // Setup the plug-ins
      if (!cmsPluginTHR(ctx, Plugin)) {
      
          cmsDeleteContext(ctx);
          return NULL;
      }
  
      return (cmsContext) ctx;  
  }
  
  // Duplicates a context with all associated plug-ins. 
  // Caller may specify an optional pointer to user-defined 
  // data that will be forwarded to plug-ins and logger. 
  cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
  {
      int i;
      struct _cmsContext_struct* ctx;
      const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
  
      void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
      
      
      ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
      if (ctx == NULL)   
          return NULL;     // Something very wrong happened
  
      // Setup default memory allocators
      memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
  
      // Maintain the linked list
      _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
         ctx ->Next = _cmsContextPoolHead;
         _cmsContextPoolHead = ctx;
      _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
  
      ctx ->chunks[UserPtr]    = userData;
      ctx ->chunks[MemPlugin]  = &ctx->DefaultMemoryManager;
  
      ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
      if (ctx ->MemPool == NULL) {
  
           cmsDeleteContext(ctx);
          return NULL;
      }
  
      // Allocate all required chunks.
      _cmsAllocLogErrorChunk(ctx, src);
      _cmsAllocAlarmCodesChunk(ctx, src);
      _cmsAllocAdaptationStateChunk(ctx, src);
      _cmsAllocMemPluginChunk(ctx, src);
      _cmsAllocInterpPluginChunk(ctx, src);
      _cmsAllocCurvesPluginChunk(ctx, src);
      _cmsAllocFormattersPluginChunk(ctx, src);
      _cmsAllocTagTypePluginChunk(ctx, src);
      _cmsAllocMPETypePluginChunk(ctx, src);
      _cmsAllocTagPluginChunk(ctx, src);
      _cmsAllocIntentsPluginChunk(ctx, src);
      _cmsAllocOptimizationPluginChunk(ctx, src);
      _cmsAllocTransformPluginChunk(ctx, src);
      _cmsAllocMutexPluginChunk(ctx, src);
  
      // Make sure no one failed
      for (i=Logger; i < MemoryClientMax; i++) {
  
          if (src ->chunks[i] == NULL) {
              cmsDeleteContext((cmsContext) ctx);
              return NULL;
          }
      }
  
      return (cmsContext) ctx;
  }
  
  
  // Frees any resources associated with the given context, 
  // and destroys the context placeholder. 
  // The ContextID can no longer be used in any THR operation.  
  void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
  {
      if (ContextID != NULL) {
  
          struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;              
          struct _cmsContext_struct  fakeContext;  
          struct _cmsContext_struct* prev;
  
          memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
  
          fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
          fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
  
          // Get rid of plugins
          cmsUnregisterPluginsTHR(ContextID); 
  
          // Since all memory is allocated in the private pool, all what we need to do is destroy the pool
          if (ctx -> MemPool != NULL)
                _cmsSubAllocDestroy(ctx ->MemPool);
          ctx -> MemPool = NULL;
  
          // Maintain list
          _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
          if (_cmsContextPoolHead == ctx) { 
  
              _cmsContextPoolHead = ctx->Next;
          }
          else {
  
              // Search for previous
              for (prev = _cmsContextPoolHead; 
                   prev != NULL;
                   prev = prev ->Next)
              {
                  if (prev -> Next == ctx) {
                      prev -> Next = ctx ->Next;
                      break;
                  }
              }
          }
          _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
  
          // free the memory block itself
          _cmsFree(&fakeContext, ctx);
      }
  }
  
  // Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
  void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
  {
      return _cmsContextGetClientChunk(ContextID, UserPtr);
  }
  
  
  