Coverage Report

Created: 2025-07-23 06:37

/src/mupdf/source/fitz/harfbuzz.c
Line
Count
Source
1
// Copyright (C) 2004-2021 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
/*
24
 * Some additional glue functions for using Harfbuzz with
25
 * custom allocators.
26
 */
27
28
#include "mupdf/fitz.h"
29
30
#if FZ_ENABLE_HTML_ENGINE
31
32
#include "hb.h"
33
34
#include <assert.h>
35
36
/* Harfbuzz has some major design flaws (for our usage
37
 * at least).
38
 *
39
 * By default it uses malloc and free as the underlying
40
 * allocators. Thus in its default form we cannot get
41
 * a record (much less control) over how much allocation
42
 * is done.
43
 *
44
 * Harfbuzz does allow build options to control where
45
 * malloc and free go - in particular we point them at
46
 * fz_hb_malloc and fz_hb_free in our implementation.
47
 * Unfortunately, this has problems too.
48
 *
49
 * Firstly, there is no mechanism for getting a context
50
 * through the call. Most other libraries allow us to
51
 * pass a "void *" value in, and have it passed through
52
 * to arrive unchanged at the allocator functions.
53
 *
54
 * Without this rudimentary functionality, we are forced
55
 * to serialise all access to Harfbuzz.
56
 *
57
 * By taking a mutex around all calls to Harfbuzz, we
58
 * can use a static of our own to get a fz_context safely
59
 * through to the allocators. This obviously costs us
60
 * performance in the multi-threaded case.
61
 *
62
 * This does not protect us against the possibility of
63
 * other people calling harfbuzz; for instance, if we
64
 * link MuPDF into an app that either calls harfbuzz
65
 * itself, or uses another library that calls harfbuzz,
66
 * there is no guarantee that that library will take
67
 * the same lock while calling harfbuzz. This leaves
68
 * us open to the possibility of crashes. The only
69
 * way around this would be to use completely separate
70
 * harfbuzz instances.
71
 *
72
 * In order to ensure that allocations throughout mupdf
73
 * are done consistently, we get harfbuzz to call our
74
 * own fz_hb_malloc/realloc/calloc/free functions that
75
 * call down to fz_malloc/realloc/calloc/free. These
76
 * require context variables, so we get our fz_hb_lock
77
 * and unlock to set these. Any attempt to call through
78
 * without setting these will be detected.
79
 *
80
 * It is therefore vital that any fz_lock/fz_unlock
81
 * handlers are shared between all the fz_contexts in
82
 * use at a time.
83
 *
84
 * Secondly, Harfbuzz allocates some 'internal' memory
85
 * on the first call, and leaves this linked from static
86
 * variables. By default, this data is never freed back.
87
 * This means it is impossible to clear the library back
88
 * to a default state. Memory debugging will always show
89
 * Harfbuzz as having leaked a set amount of memory.
90
 *
91
 * There is a mechanism in Harfbuzz for freeing these
92
 * blocks - that of building with HAVE_ATEXIT. This
93
 * causes the blocks to be freed back on exit, but a)
94
 * this doesn't reset the fz_context value, so we can't
95
 * free them correctly, and b) any fz_context value it
96
 * did keep would already have been closed down due to
97
 * the program exit.
98
 *
99
 * In addition, because of these everlasting blocks, we
100
 * cannot safely call Harfbuzz after we close down any
101
 * allocator that Harfbuzz has been using (because
102
 * Harfbuzz may still be holding pointers to data within
103
 * that allocators managed space).
104
 *
105
 * There is nothing we can do about the leaking blocks
106
 * except to add some hacks to our memory debugging
107
 * library to allow it to suppress the blocks that
108
 * harfbuzz leaks.
109
 *
110
 * Consequently, we leave them to leak, and warn Memento
111
 * about this.
112
 */
113
114
/* Potentially we can write different versions
115
 * of get_context and set_context for different
116
 * threading systems.
117
 *
118
 * This simple version relies on harfbuzz never
119
 * trying to make 2 allocations at once on
120
 * different threads. The only way that can happen
121
 * is when one of those other threads is someone
122
 * outside MuPDF calling harfbuzz while MuPDF
123
 * is running. This will cause us such huge
124
 * problems that for now, we'll just forbid it.
125
 */
126
127
static fz_context *fz_hb_secret = NULL;
128
129
static void set_hb_context(fz_context *ctx)
130
23.6k
{
131
23.6k
  fz_hb_secret = ctx;
132
23.6k
}
133
134
static fz_context *get_hb_context(void)
135
27.5k
{
136
27.5k
  return fz_hb_secret;
137
27.5k
}
138
139
void fz_hb_lock(fz_context *ctx)
140
11.8k
{
141
11.8k
  fz_ft_lock(ctx);
142
143
11.8k
  set_hb_context(ctx);
144
11.8k
}
145
146
void fz_hb_unlock(fz_context *ctx)
147
11.8k
{
148
11.8k
  set_hb_context(NULL);
149
150
11.8k
  fz_ft_unlock(ctx);
151
11.8k
}
152
153
void *fz_hb_malloc(size_t size)
154
1
{
155
1
  fz_context *ctx = get_hb_context();
156
157
1
  assert(ctx != NULL);
158
159
1
  return Memento_label(fz_malloc_no_throw(ctx, size), "hb");
160
1
}
161
162
void *fz_hb_calloc(size_t n, size_t size)
163
2.06k
{
164
2.06k
  fz_context *ctx = get_hb_context();
165
166
2.06k
  assert(ctx != NULL);
167
168
2.06k
  return Memento_label(fz_calloc_no_throw(ctx, n, size), "hb");
169
2.06k
}
170
171
void *fz_hb_realloc(void *ptr, size_t size)
172
5.05k
{
173
5.05k
  fz_context *ctx = get_hb_context();
174
175
5.05k
  assert(ctx != NULL);
176
177
5.05k
  return Memento_label(fz_realloc_no_throw(ctx, ptr, size), "hb");
178
5.05k
}
179
180
void fz_hb_free(void *ptr)
181
20.4k
{
182
20.4k
  fz_context *ctx = get_hb_context();
183
184
20.4k
  assert(ctx != NULL);
185
186
20.4k
  fz_free(ctx, ptr);
187
20.4k
}
188
189
#endif