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