1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  3  *
  4  * This code is free software; you can redistribute it and/or modify it
  5  * under the terms of the GNU General Public License version 2 only, as
  6  * published by the Free Software Foundation.  Oracle designates this
  7  * particular file as subject to the "Classpath" exception as provided
  8  * by Oracle in the LICENSE file that accompanied this code.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  */
 24 
 25 // This file is available under and governed by the GNU General Public
 26 // License version 2 only, as published by the Free Software Foundation.
 27 // However, the following notice accompanied the original version of this
 28 // file:
 29 //
 30 //---------------------------------------------------------------------------------
 31 //
 32 //  Little Color Management System
 33 //  Copyright (c) 1998-2023 Marti Maria Saguer
 34 //
 35 // Permission is hereby granted, free of charge, to any person obtaining
 36 // a copy of this software and associated documentation files (the "Software"),
 37 // to deal in the Software without restriction, including without limitation
 38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
 39 // and/or sell copies of the Software, and to permit persons to whom the Software
 40 // is furnished to do so, subject to the following conditions:
 41 //
 42 // The above copyright notice and this permission notice shall be included in
 43 // all copies or substantial portions of the Software.
 44 //
 45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 52 //
 53 //---------------------------------------------------------------------------------
 54 
 55 #include "lcms2_internal.h"
 56 
 57 
 58 // This function is here to help applications to prevent mixing lcms versions on header and shared objects.
 59 int CMSEXPORT cmsGetEncodedCMMversion(void)
 60 {
 61        return LCMS_VERSION;
 62 }
 63 
 64 // I am so tired about incompatibilities on those functions that here are some replacements
 65 // that hopefully would be fully portable.
 66 
 67 // compare two strings ignoring case
 68 int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2)
 69 {
 70     CMSREGISTER const unsigned char *us1 = (const unsigned char *)s1,
 71                                  *us2 = (const unsigned char *)s2;
 72 
 73     while (toupper(*us1) == toupper(*us2++))
 74         if (*us1++ == '\0')
 75             return 0;
 76 
 77     return (toupper(*us1) - toupper(*--us2));
 78 }
 79 
 80 // long int because C99 specifies ftell in such way (7.19.9.2)
 81 long int CMSEXPORT cmsfilelength(FILE* f)
 82 {
 83     long int p , n;
 84 
 85     p = ftell(f); // register current file position
 86     if (p == -1L)
 87         return -1L;
 88 
 89     if (fseek(f, 0, SEEK_END) != 0) {
 90         return -1L;
 91     }
 92 
 93     n = ftell(f);
 94     fseek(f, p, SEEK_SET); // file position restored
 95 
 96     return n;
 97 }
 98 
 99 
100 // Memory handling ------------------------------------------------------------------
101 //
102 // This is the interface to low-level memory management routines. By default a simple
103 // wrapping to malloc/free/realloc is provided, although there is a limit on the max
104 // amount of memory that can be reclaimed. This is mostly as a safety feature to prevent
105 // bogus or evil code to allocate huge blocks that otherwise lcms would never need.
106 
107 #define MAX_MEMORY_FOR_ALLOC  ((cmsUInt32Number)(1024U*1024U*512U))
108 
109 // User may override this behaviour by using a memory plug-in, which basically replaces
110 // the default memory management functions. In this case, no check is performed and it
111 // is up to the plug-in writer to keep in the safe side. There are only three functions
112 // required to be implemented: malloc, realloc and free, although the user may want to
113 // replace the optional mallocZero, calloc and dup as well.
114 
115 cmsBool   _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
116 
117 // *********************************************************************************
118 
119 // This is the default memory allocation function. It does a very coarse
120 // check of amount of memory, just to prevent exploits
121 static
122 void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size)
123 {
124     // Never allow 0 or over maximum
125     if (size == 0 || size > MAX_MEMORY_FOR_ALLOC) return NULL;
126 
127     return (void*) malloc(size);
128 
129     cmsUNUSED_PARAMETER(ContextID);
130 }
131 
132 // Generic allocate & zero
133 static
134 void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size)
135 {
136     void *pt = _cmsMalloc(ContextID, size);
137     if (pt == NULL) return NULL;
138 
139     memset(pt, 0, size);
140     return pt;
141 }
142 
143 
144 // The default free function. The only check proformed is against NULL pointers
145 static
146 void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr)
147 {
148     // free(NULL) is defined a no-op by C99, therefore it is safe to
149     // avoid the check, but it is here just in case...
150 
151     if (Ptr) free(Ptr);
152 
153     cmsUNUSED_PARAMETER(ContextID);
154 }
155 
156 // The default realloc function. Again it checks for exploits. If Ptr is NULL,
157 // realloc behaves the same way as malloc and allocates a new block of size bytes.
158 static
159 void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
160 {
161 
162     if (size > MAX_MEMORY_FOR_ALLOC) return NULL;  // Never realloc over 512Mb
163 
164     return realloc(Ptr, size);
165 
166     cmsUNUSED_PARAMETER(ContextID);
167 }
168 
169 
170 // The default calloc function. Allocates an array of num elements, each one of size bytes
171 // all memory is initialized to zero.
172 static
173 void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)
174 {
175     cmsUInt32Number Total = num * size;
176 
177     // Preserve calloc behaviour
178     if (Total == 0) return NULL;
179 
180     // Safe check for overflow.
181     if (num >= UINT_MAX / size) return NULL;
182 
183     // Check for overflow
184     if (Total < num || Total < size) {
185         return NULL;
186     }
187 
188     if (Total > MAX_MEMORY_FOR_ALLOC) return NULL;  // Never alloc over 512Mb
189 
190     return _cmsMallocZero(ContextID, Total);
191 }
192 
193 // Generic block duplication
194 static
195 void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size)
196 {
197     void* mem;
198 
199     if (size > MAX_MEMORY_FOR_ALLOC) return NULL;  // Never dup over 512Mb
200 
201     mem = _cmsMalloc(ContextID, size);
202 
203     if (mem != NULL && Org != NULL)
204         memmove(mem, Org, size);
205 
206     return mem;
207 }
208 
209 
210 // Pointers to memory manager functions in Context0
211 _cmsMemPluginChunkType _cmsMemPluginChunk = { _cmsMallocDefaultFn, _cmsMallocZeroDefaultFn, _cmsFreeDefaultFn,
212                                               _cmsReallocDefaultFn, _cmsCallocDefaultFn,    _cmsDupDefaultFn
213                                             };
214 
215 
216 // Reset and duplicate memory manager
217 void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src)
218 {
219     _cmsAssert(ctx != NULL);
220 
221     if (src != NULL) {
222 
223         // Duplicate
224         ctx ->chunks[MemPlugin] = _cmsSubAllocDup(ctx ->MemPool, src ->chunks[MemPlugin], sizeof(_cmsMemPluginChunkType));
225     }
226     else {
227 
228         // To reset it, we use the default allocators, which cannot be overridden
229         ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager;
230     }
231 }
232 
233 // Auxiliary to fill memory management functions from plugin (or context 0 defaults)
234 void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr)
235 {
236     if (Plugin == NULL) {
237 
238         memcpy(ptr, &_cmsMemPluginChunk, sizeof(_cmsMemPluginChunk));
239     }
240     else {
241 
242         ptr ->MallocPtr  = Plugin -> MallocPtr;
243         ptr ->FreePtr    = Plugin -> FreePtr;
244         ptr ->ReallocPtr = Plugin -> ReallocPtr;
245 
246         // Make sure we revert to defaults
247         ptr ->MallocZeroPtr= _cmsMallocZeroDefaultFn;
248         ptr ->CallocPtr    = _cmsCallocDefaultFn;
249         ptr ->DupPtr       = _cmsDupDefaultFn;
250 
251         if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr;
252         if (Plugin ->CallocPtr != NULL)     ptr ->CallocPtr     = Plugin -> CallocPtr;
253         if (Plugin ->DupPtr != NULL)        ptr ->DupPtr        = Plugin -> DupPtr;
254 
255     }
256 }
257 
258 
259 // Plug-in replacement entry
260 cmsBool  _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data)
261 {
262     cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data;
263     _cmsMemPluginChunkType* ptr;
264 
265     // NULL forces to reset to defaults. In this special case, the defaults are stored in the context structure.
266     // Remaining plug-ins does NOT have any copy in the context structure, but this is somehow special as the
267     // context internal data should be malloc'ed by using those functions.
268     if (Data == NULL) {
269 
270        struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID;
271 
272        // Return to the default allocators
273         if (ContextID != NULL) {
274             ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager;
275         }
276         return TRUE;
277     }
278 
279     // Check for required callbacks
280     if (Plugin -> MallocPtr == NULL ||
281         Plugin -> FreePtr == NULL ||
282         Plugin -> ReallocPtr == NULL) return FALSE;
283 
284     // Set replacement functions
285     ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
286     if (ptr == NULL)
287         return FALSE;
288 
289     _cmsInstallAllocFunctions(Plugin, ptr);
290     return TRUE;
291 }
292 
293 // Generic allocate
294 void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size)
295 {
296     _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
297     return ptr ->MallocPtr(ContextID, size);
298 }
299 
300 // Generic allocate & zero
301 void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size)
302 {
303     _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
304     return ptr->MallocZeroPtr(ContextID, size);
305 }
306 
307 // Generic calloc
308 void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)
309 {
310     _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
311     return ptr->CallocPtr(ContextID, num, size);
312 }
313 
314 // Generic reallocate
315 void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
316 {
317     _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
318     return ptr->ReallocPtr(ContextID, Ptr, size);
319 }
320 
321 // Generic free memory
322 void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr)
323 {
324     if (Ptr != NULL) {
325         _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
326         ptr ->FreePtr(ContextID, Ptr);
327     }
328 }
329 
330 // Generic block duplication
331 void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size)
332 {
333     _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
334     return ptr ->DupPtr(ContextID, Org, size);
335 }
336 
337 // ********************************************************************************************
338 
339 // Sub allocation takes care of many pointers of small size. The memory allocated in
340 // this way have be freed at once. Next function allocates a single chunk for linked list
341 // I prefer this method over realloc due to the big impact on xput realloc may have if
342 // memory is being swapped to disk. This approach is safer (although that may not be true on all platforms)
343 static
344 _cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial)
345 {
346     _cmsSubAllocator_chunk* chunk;
347 
348     // 20K by default
349     if (Initial == 0)
350         Initial = 20*1024;
351 
352     // Create the container
353     chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk));
354     if (chunk == NULL) return NULL;
355 
356     // Initialize values
357     chunk ->Block     = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial);
358     if (chunk ->Block == NULL) {
359 
360         // Something went wrong
361         _cmsFree(ContextID, chunk);
362         return NULL;
363     }
364 
365     chunk ->BlockSize = Initial;
366     chunk ->Used      = 0;
367     chunk ->next      = NULL;
368 
369     return chunk;
370 }
371 
372 // The suballocated is nothing but a pointer to the first element in the list. We also keep
373 // the thread ID in this structure.
374 _cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial)
375 {
376     _cmsSubAllocator* sub;
377 
378     // Create the container
379     sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator));
380     if (sub == NULL) return NULL;
381 
382     sub ->ContextID = ContextID;
383 
384     sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial);
385     if (sub ->h == NULL) {
386         _cmsFree(ContextID, sub);
387         return NULL;
388     }
389 
390     return sub;
391 }
392 
393 
394 // Get rid of whole linked list
395 void _cmsSubAllocDestroy(_cmsSubAllocator* sub)
396 {
397     _cmsSubAllocator_chunk *chunk, *n;
398 
399     for (chunk = sub ->h; chunk != NULL; chunk = n) {
400 
401         n = chunk->next;
402         if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block);
403         _cmsFree(sub ->ContextID, chunk);
404     }
405 
406     // Free the header
407     _cmsFree(sub ->ContextID, sub);
408 }
409 
410 
411 // Get a pointer to small memory block.
412 void*  _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
413 {
414     cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used;
415     cmsUInt8Number* ptr;
416 
417     size = _cmsALIGNMEM(size);
418 
419     // Check for memory. If there is no room, allocate a new chunk of double memory size.
420     if (size > Free) {
421 
422         _cmsSubAllocator_chunk* chunk;
423         cmsUInt32Number newSize;
424 
425         newSize = sub -> h ->BlockSize * 2;
426         if (newSize < size) newSize = size;
427 
428         chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize);
429         if (chunk == NULL) return NULL;
430 
431         // Link list
432         chunk ->next = sub ->h;
433         sub ->h    = chunk;
434 
435     }
436 
437     ptr =  sub -> h ->Block + sub -> h ->Used;
438     sub -> h -> Used += size;
439 
440     return (void*) ptr;
441 }
442 
443 // Duplicate in pool
444 void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size)
445 {
446     void *NewPtr;
447 
448     // Dup of null pointer is also NULL
449     if (ptr == NULL)
450         return NULL;
451 
452     NewPtr = _cmsSubAlloc(s, size);
453 
454     if (ptr != NULL && NewPtr != NULL) {
455         memcpy(NewPtr, ptr, size);
456     }
457 
458     return NewPtr;
459 }
460 
461 
462 
463 // Error logging ******************************************************************
464 
465 // There is no error handling at all. When a function fails, it returns proper value.
466 // For example, all create functions does return NULL on failure. Other return FALSE
467 // It may be interesting, for the developer, to know why the function is failing.
468 // for that reason, lcms2 does offer a logging function. This function does receive
469 // a ENGLISH string with some clues on what is going wrong. You can show this
470 // info to the end user, or just create some sort of log.
471 // The logging function should NOT terminate the program, as this obviously can leave
472 // resources. It is the programmer's responsibility to check each function return code
473 // to make sure it didn't fail.
474 
475 // Error messages are limited to MAX_ERROR_MESSAGE_LEN
476 
477 #define MAX_ERROR_MESSAGE_LEN   1024
478 
479 // ---------------------------------------------------------------------------------------------------------
480 
481 // This is our default log error
482 static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text);
483 
484 // Context0 storage, which is global
485 _cmsLogErrorChunkType _cmsLogErrorChunk = { DefaultLogErrorHandlerFunction };
486 
487 // Allocates and inits error logger container for a given context. If src is NULL, only initializes the value
488 // to the default. Otherwise, it duplicates the value. The interface is standard across all context clients
489 void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx,
490                             const struct _cmsContext_struct* src)
491 {
492     static _cmsLogErrorChunkType LogErrorChunk = { DefaultLogErrorHandlerFunction };
493     void* from;
494 
495      if (src != NULL) {
496         from = src ->chunks[Logger];
497     }
498     else {
499        from = &LogErrorChunk;
500     }
501 
502     ctx ->chunks[Logger] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsLogErrorChunkType));
503 }
504 
505 // The default error logger does nothing.
506 static
507 void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
508 {
509     // fprintf(stderr, "[lcms]: %s\n", Text);
510     // fflush(stderr);
511 
512      cmsUNUSED_PARAMETER(ContextID);
513      cmsUNUSED_PARAMETER(ErrorCode);
514      cmsUNUSED_PARAMETER(Text);
515 }
516 
517 // Change log error, context based
518 void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn)
519 {
520     _cmsLogErrorChunkType* lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger);
521 
522     if (lhg != NULL) {
523 
524         if (Fn == NULL)
525             lhg -> LogErrorHandler = DefaultLogErrorHandlerFunction;
526         else
527             lhg -> LogErrorHandler = Fn;
528     }
529 }
530 
531 // Change log error, legacy
532 void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn)
533 {
534     cmsSetLogErrorHandlerTHR(NULL, Fn);
535 }
536 
537 // Log an error
538 // ErrorText is a text holding an english description of error.
539 void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...)
540 {
541     va_list args;
542     char Buffer[MAX_ERROR_MESSAGE_LEN];
543     _cmsLogErrorChunkType* lhg;
544 
545 
546     va_start(args, ErrorText);
547     vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args);
548     va_end(args);
549 
550     // Check for the context, if specified go there. If not, go for the global
551     lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger);
552     if (lhg ->LogErrorHandler) {
553         lhg ->LogErrorHandler(ContextID, ErrorCode, Buffer);
554     }
555 }
556 
557 // Utility function to print signatures
558 void _cmsTagSignature2String(char String[5], cmsTagSignature sig)
559 {
560     cmsUInt32Number be;
561 
562     // Convert to big endian
563     be = _cmsAdjustEndianess32((cmsUInt32Number) sig);
564 
565     // Move chars
566     memmove(String, &be, 4);
567 
568     // Make sure of terminator
569     String[4] = 0;
570 }
571 
572 //--------------------------------------------------------------------------------------------------
573 
574 
575 static
576 void* defMtxCreate(cmsContext id)
577 {
578     _cmsMutex* ptr_mutex = (_cmsMutex*) _cmsMalloc(id, sizeof(_cmsMutex));
579     _cmsInitMutexPrimitive(ptr_mutex);
580     return (void*) ptr_mutex;
581 }
582 
583 static
584 void defMtxDestroy(cmsContext id, void* mtx)
585 {
586     _cmsDestroyMutexPrimitive((_cmsMutex *) mtx);
587     _cmsFree(id, mtx);
588 }
589 
590 static
591 cmsBool defMtxLock(cmsContext id, void* mtx)
592 {
593     cmsUNUSED_PARAMETER(id);
594     return _cmsLockPrimitive((_cmsMutex *) mtx) == 0;
595 }
596 
597 static
598 void defMtxUnlock(cmsContext id, void* mtx)
599 {
600     cmsUNUSED_PARAMETER(id);
601     _cmsUnlockPrimitive((_cmsMutex *) mtx);
602 }
603 
604 
605 
606 // Pointers to memory manager functions in Context0
607 _cmsMutexPluginChunkType _cmsMutexPluginChunk = { defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock };
608 
609 // Allocate and init mutex container.
610 void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx,
611                                         const struct _cmsContext_struct* src)
612 {
613     static _cmsMutexPluginChunkType MutexChunk = {defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock };
614     void* from;
615 
616      if (src != NULL) {
617         from = src ->chunks[MutexPlugin];
618     }
619     else {
620        from = &MutexChunk;
621     }
622 
623     ctx ->chunks[MutexPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsMutexPluginChunkType));
624 }
625 
626 // Register new ways to transform
627 cmsBool  _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Data)
628 {
629     cmsPluginMutex* Plugin = (cmsPluginMutex*) Data;
630     _cmsMutexPluginChunkType* ctx = ( _cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
631 
632     if (Data == NULL) {
633 
634         // No lock routines
635         ctx->CreateMutexPtr = NULL;
636         ctx->DestroyMutexPtr = NULL;
637         ctx->LockMutexPtr = NULL;
638         ctx ->UnlockMutexPtr = NULL;
639         return TRUE;
640     }
641 
642     // Factory callback is required
643     if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL ||
644         Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE;
645 
646     ctx->CreateMutexPtr  = Plugin->CreateMutexPtr;
647     ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr;
648     ctx ->LockMutexPtr   = Plugin ->LockMutexPtr;
649     ctx ->UnlockMutexPtr = Plugin ->UnlockMutexPtr;
650 
651     // All is ok
652     return TRUE;
653 }
654 
655 // Generic Mutex fns
656 void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID)
657 {
658     _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
659 
660     if (ptr ->CreateMutexPtr == NULL) return NULL;
661 
662     return ptr ->CreateMutexPtr(ContextID);
663 }
664 
665 void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx)
666 {
667     _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
668 
669     if (ptr ->DestroyMutexPtr != NULL) {
670 
671         ptr ->DestroyMutexPtr(ContextID, mtx);
672     }
673 }
674 
675 cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx)
676 {
677     _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
678 
679     if (ptr ->LockMutexPtr == NULL) return TRUE;
680 
681     return ptr ->LockMutexPtr(ContextID, mtx);
682 }
683 
684 void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx)
685 {
686     _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
687 
688     if (ptr ->UnlockMutexPtr != NULL) {
689 
690         ptr ->UnlockMutexPtr(ContextID, mtx);
691     }
692 }
693 
694 // The global Context0 storage for parallelization plug-in
695  _cmsParallelizationPluginChunkType _cmsParallelizationPluginChunk = { 0 };
696 
697 // Allocate parallelization container.
698 void _cmsAllocParallelizationPluginChunk(struct _cmsContext_struct* ctx,
699                                          const struct _cmsContext_struct* src)
700 {
701     if (src != NULL) {
702         void* from = src->chunks[ParallelizationPlugin];
703         ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, from, sizeof(_cmsParallelizationPluginChunkType));
704     }
705     else {
706         _cmsParallelizationPluginChunkType ParallelizationPluginChunk = { 0 };
707         ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &ParallelizationPluginChunk, sizeof(_cmsParallelizationPluginChunkType));
708     }
709 }
710 
711 // Register parallel processing
712 cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Data)
713 {
714     cmsPluginParalellization* Plugin = (cmsPluginParalellization*)Data;
715     _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(ContextID, ParallelizationPlugin);
716 
717     if (Data == NULL) {
718 
719         // No parallelization routines
720         ctx->MaxWorkers = 0;
721         ctx->WorkerFlags = 0;
722         ctx->SchedulerFn = NULL;
723         return TRUE;
724     }
725 
726     // callback is required
727     if (Plugin->SchedulerFn == NULL) return FALSE;
728 
729     ctx->MaxWorkers = Plugin->MaxWorkers;
730     ctx->WorkerFlags = Plugin->WorkerFlags;
731     ctx->SchedulerFn = Plugin->SchedulerFn;
732 
733     // All is ok
734     return TRUE;
735 }
736