Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/fitz/memory.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/memory.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,477 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +/* Enable FITZ_DEBUG_LOCKING_TIMES below if you want to check the times + * for which locks are held too. */ +#ifdef FITZ_DEBUG_LOCKING +#undef FITZ_DEBUG_LOCKING_TIMES +#endif + +/* + * The malloc family of functions will always try scavenging when they run out of memory. + * They will only fail when scavenging cannot free up memory from caches in the fz_context. + * All the functions will throw an exception when no memory can be allocated, + * except the _no_throw family which instead silently returns NULL. + */ + +static void * +do_scavenging_malloc(fz_context *ctx, size_t size) +{ + void *p; + int phase = 0; + + fz_lock(ctx, FZ_LOCK_ALLOC); + do { + p = ctx->alloc.malloc(ctx->alloc.user, size); + if (p != NULL) + { + fz_unlock(ctx, FZ_LOCK_ALLOC); + return p; + } + } while (fz_store_scavenge(ctx, size, &phase)); + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; +} + +static void * +do_scavenging_realloc(fz_context *ctx, void *p, size_t size) +{ + void *q; + int phase = 0; + + fz_lock(ctx, FZ_LOCK_ALLOC); + do { + q = ctx->alloc.realloc(ctx->alloc.user, p, size); + if (q != NULL) + { + fz_unlock(ctx, FZ_LOCK_ALLOC); + return q; + } + } while (fz_store_scavenge(ctx, size, &phase)); + fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; +} + +void * +fz_malloc(fz_context *ctx, size_t size) +{ + void *p; + if (size == 0) + return NULL; + p = do_scavenging_malloc(ctx, size); + if (!p) + { + errno = ENOMEM; + fz_throw(ctx, FZ_ERROR_SYSTEM, "malloc (%zu bytes) failed", size); + } + return p; +} + +void * +fz_malloc_no_throw(fz_context *ctx, size_t size) +{ + if (size == 0) + return NULL; + return do_scavenging_malloc(ctx, size); +} + +void * +fz_calloc(fz_context *ctx, size_t count, size_t size) +{ + void *p; + if (count == 0 || size == 0) + return NULL; + if (count > SIZE_MAX / size) + fz_throw(ctx, FZ_ERROR_LIMIT, "calloc (%zu x %zu bytes) failed (size_t overflow)", count, size); + p = do_scavenging_malloc(ctx, count * size); + if (!p) + { + errno = ENOMEM; + fz_throw(ctx, FZ_ERROR_SYSTEM, "calloc (%zu x %zu bytes) failed", count, size); + } + memset(p, 0, count*size); + return p; +} + +void * +fz_calloc_no_throw(fz_context *ctx, size_t count, size_t size) +{ + void *p; + if (count == 0 || size == 0) + return NULL; + if (count > SIZE_MAX / size) + return NULL; + p = do_scavenging_malloc(ctx, count * size); + if (p) + memset(p, 0, count * size); + return p; +} + +void * +fz_realloc(fz_context *ctx, void *p, size_t size) +{ + if (size == 0) + { + fz_free(ctx, p); + return NULL; + } + p = do_scavenging_realloc(ctx, p, size); + if (!p) + { + errno = ENOMEM; + fz_throw(ctx, FZ_ERROR_SYSTEM, "realloc (%zu bytes) failed", size); + } + return p; +} + +void * +fz_realloc_no_throw(fz_context *ctx, void *p, size_t size) +{ + if (size == 0) + { + fz_free(ctx, p); + return NULL; + } + return do_scavenging_realloc(ctx, p, size); +} + +void +fz_free(fz_context *ctx, void *p) +{ + if (p) + { + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->alloc.free(ctx->alloc.user, p); + fz_unlock(ctx, FZ_LOCK_ALLOC); + } +} + +/* align is assumed to be a power of 2. */ +void *fz_malloc_aligned(fz_context *ctx, size_t size, int align) +{ + uint8_t *block; + uint8_t *aligned; + + if (size == 0) + return NULL; + + if (align >= 256) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Alignment too large"); + if ((align & (align-1)) != 0) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Alignment must be a power of 2"); + + block = fz_malloc(ctx, size + align); + + aligned = (void *)((intptr_t)(block + align-1) & ~(align-1)); + if (aligned == block) + aligned = block + align; + memset(block, aligned-block, aligned-block); + + return aligned; +} + +void fz_free_aligned(fz_context *ctx, void *ptr) +{ + uint8_t *block = ptr; + + if (ptr == NULL) + return; + + block -= block[-1]; + + fz_free(ctx, block); +} + +char * +fz_strdup(fz_context *ctx, const char *s) +{ + size_t len = strlen(s) + 1; + char *ns = fz_malloc(ctx, len); + memcpy(ns, s, len); + return ns; +} + +fz_string * +fz_new_string(fz_context *ctx, const char *s) +{ + fz_string *str = fz_malloc_flexible(ctx, fz_string, str, strlen(s) + 1); + str->refs = 1; + strcpy(str->str, s); + return str; +} + +fz_string *fz_keep_string(fz_context *ctx, fz_string *str) +{ + return fz_keep_imp(ctx, str, &str->refs); +} + +void fz_drop_string(fz_context *ctx, fz_string *str) +{ + if (fz_drop_imp(ctx, str, &str->refs)) + fz_free(ctx, str); +} + + +static void * +fz_malloc_default(void *opaque, size_t size) +{ + return malloc(size); +} + +static void * +fz_realloc_default(void *opaque, void *old, size_t size) +{ + return realloc(old, size); +} + +static void +fz_free_default(void *opaque, void *ptr) +{ + free(ptr); +} + +fz_alloc_context fz_alloc_default = +{ + NULL, + fz_malloc_default, + fz_realloc_default, + fz_free_default +}; + +static void +fz_lock_default(void *user, int lock) +{ +} + +static void +fz_unlock_default(void *user, int lock) +{ +} + +fz_locks_context fz_locks_default = +{ + NULL, + fz_lock_default, + fz_unlock_default +}; + +#ifdef FITZ_DEBUG_LOCKING + +enum +{ + FZ_LOCK_DEBUG_CONTEXT_MAX = 100 +}; + +fz_context *fz_lock_debug_contexts[FZ_LOCK_DEBUG_CONTEXT_MAX]; +int fz_locks_debug[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX]; + +#ifdef FITZ_DEBUG_LOCKING_TIMES + +int fz_debug_locking_inited = 0; +int fz_lock_program_start; +int fz_lock_time[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX] = { { 0 } }; +int fz_lock_taken[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX] = { { 0 } }; + +/* We implement our own millisecond clock, as clock() cannot be trusted + * when threads are involved. */ +static int ms_clock(void) +{ +#ifdef _WIN32 + return (int)GetTickCount(); +#else + struct timeval tp; + gettimeofday(&tp, NULL); + return (tp.tv_sec*1000) + (tp.tv_usec/1000); +#endif +} + +static void dump_lock_times(void) +{ + int i, j; + int prog_time = ms_clock() - fz_lock_program_start; + + for (j = 0; j < FZ_LOCK_MAX; j++) + { + int total = 0; + for (i = 0; i < FZ_LOCK_DEBUG_CONTEXT_MAX; i++) + { + total += fz_lock_time[i][j]; + } + printf("Lock %d held for %g seconds (%g%%)\n", j, total / 1000.0f, 100.0f*total/prog_time); + } + printf("Total program time %g seconds\n", prog_time / 1000.0f); +} + +#endif + +static int find_context(fz_context *ctx) +{ + int i; + + for (i = 0; i < FZ_LOCK_DEBUG_CONTEXT_MAX; i++) + { + if (fz_lock_debug_contexts[i] == ctx) + return i; + if (fz_lock_debug_contexts[i] == NULL) + { + int gottit = 0; + /* We've not locked on this context before, so use + * this one for this new context. We might have other + * threads trying here too though so, so claim it + * atomically. No one has locked on this context + * before, so we are safe to take the ALLOC lock. */ + ctx->locks.lock(ctx->locks.user, FZ_LOCK_ALLOC); + /* If it's still free, then claim it as ours, + * otherwise we'll keep hunting. */ + if (fz_lock_debug_contexts[i] == NULL) + { + gottit = 1; + fz_lock_debug_contexts[i] = ctx; +#ifdef FITZ_DEBUG_LOCKING_TIMES + if (fz_debug_locking_inited == 0) + { + fz_debug_locking_inited = 1; + fz_lock_program_start = ms_clock(); + atexit(dump_lock_times); + } +#endif + } + ctx->locks.unlock(ctx->locks.user, FZ_LOCK_ALLOC); + if (gottit) + return i; + } + } + return -1; +} + +void +fz_assert_lock_held(fz_context *ctx, int lock) +{ + int idx; + + if (ctx->locks.lock != fz_lock_default) + return; + + idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] == 0) + fprintf(stderr, "Lock %d not held when expected\n", lock); +} + +void +fz_assert_lock_not_held(fz_context *ctx, int lock) +{ + int idx; + + if (ctx->locks.lock != fz_lock_default) + return; + + idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] != 0) + fprintf(stderr, "Lock %d held when not expected\n", lock); +} + +void fz_lock_debug_lock(fz_context *ctx, int lock) +{ + int i, idx; + + if (ctx->locks.lock != fz_lock_default) + return; + + idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] != 0) + { + fprintf(stderr, "Attempt to take lock %d when held already!\n", lock); + } + for (i = lock-1; i >= 0; i--) + { + if (fz_locks_debug[idx][i] != 0) + { + fprintf(stderr, "Lock ordering violation: Attempt to take lock %d when %d held already!\n", lock, i); + } + } + fz_locks_debug[idx][lock] = 1; +#ifdef FITZ_DEBUG_LOCKING_TIMES + fz_lock_taken[idx][lock] = ms_clock(); +#endif +} + +void fz_lock_debug_unlock(fz_context *ctx, int lock) +{ + int idx; + + if (ctx->locks.lock != fz_lock_default) + return; + + idx = find_context(ctx); + if (idx < 0) + return; + + if (fz_locks_debug[idx][lock] == 0) + { + fprintf(stderr, "Attempt to release lock %d when not held!\n", lock); + } + fz_locks_debug[idx][lock] = 0; +#ifdef FITZ_DEBUG_LOCKING_TIMES + fz_lock_time[idx][lock] += ms_clock() - fz_lock_taken[idx][lock]; +#endif +} + +#else + +void +(fz_assert_lock_held)(fz_context *ctx, int lock) +{ +} + +void +(fz_assert_lock_not_held)(fz_context *ctx, int lock) +{ +} + +void (fz_lock_debug_lock)(fz_context *ctx, int lock) +{ +} + +void (fz_lock_debug_unlock)(fz_context *ctx, int lock) +{ +} + +#endif
