/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 | | #include "hb.h" |
31 | | |
32 | | #include <assert.h> |
33 | | |
34 | | /* Harfbuzz has some major design flaws (for our usage |
35 | | * at least). |
36 | | * |
37 | | * By default it uses malloc and free as the underlying |
38 | | * allocators. Thus in its default form we cannot get |
39 | | * a record (much less control) over how much allocation |
40 | | * is done. |
41 | | * |
42 | | * Harfbuzz does allow build options to control where |
43 | | * malloc and free go - in particular we point them at |
44 | | * fz_hb_malloc and fz_hb_free in our implementation. |
45 | | * Unfortunately, this has problems too. |
46 | | * |
47 | | * Firstly, there is no mechanism for getting a context |
48 | | * through the call. Most other libraries allow us to |
49 | | * pass a "void *" value in, and have it passed through |
50 | | * to arrive unchanged at the allocator functions. |
51 | | * |
52 | | * Without this rudimentary functionality, we are forced |
53 | | * to serialise all access to Harfbuzz. |
54 | | * |
55 | | * By taking a mutex around all calls to Harfbuzz, we |
56 | | * can use a static of our own to get a fz_context safely |
57 | | * through to the allocators. This obviously costs us |
58 | | * performance in the multi-threaded case. |
59 | | * |
60 | | * This does not protect us against the possibility of |
61 | | * other people calling harfbuzz; for instance, if we |
62 | | * link MuPDF into an app that either calls harfbuzz |
63 | | * itself, or uses another library that calls harfbuzz, |
64 | | * there is no guarantee that that library will take |
65 | | * the same lock while calling harfbuzz. This leaves |
66 | | * us open to the possibility of crashes. The only |
67 | | * way around this would be to use completely separate |
68 | | * harfbuzz instances. |
69 | | * |
70 | | * In order to ensure that allocations throughout mupdf |
71 | | * are done consistently, we get harfbuzz to call our |
72 | | * own fz_hb_malloc/realloc/calloc/free functions that |
73 | | * call down to fz_malloc/realloc/calloc/free. These |
74 | | * require context variables, so we get our fz_hb_lock |
75 | | * and unlock to set these. Any attempt to call through |
76 | | * without setting these will be detected. |
77 | | * |
78 | | * It is therefore vital that any fz_lock/fz_unlock |
79 | | * handlers are shared between all the fz_contexts in |
80 | | * use at a time. |
81 | | * |
82 | | * Secondly, Harfbuzz allocates some 'internal' memory |
83 | | * on the first call, and leaves this linked from static |
84 | | * variables. By default, this data is never freed back. |
85 | | * This means it is impossible to clear the library back |
86 | | * to a default state. Memory debugging will always show |
87 | | * Harfbuzz as having leaked a set amount of memory. |
88 | | * |
89 | | * There is a mechanism in Harfbuzz for freeing these |
90 | | * blocks - that of building with HAVE_ATEXIT. This |
91 | | * causes the blocks to be freed back on exit, but a) |
92 | | * this doesn't reset the fz_context value, so we can't |
93 | | * free them correctly, and b) any fz_context value it |
94 | | * did keep would already have been closed down due to |
95 | | * the program exit. |
96 | | * |
97 | | * In addition, because of these everlasting blocks, we |
98 | | * cannot safely call Harfbuzz after we close down any |
99 | | * allocator that Harfbuzz has been using (because |
100 | | * Harfbuzz may still be holding pointers to data within |
101 | | * that allocators managed space). |
102 | | * |
103 | | * There is nothing we can do about the leaking blocks |
104 | | * except to add some hacks to our memory debugging |
105 | | * library to allow it to suppress the blocks that |
106 | | * harfbuzz leaks. |
107 | | * |
108 | | * Consequently, we leave them to leak, and warn Memento |
109 | | * about this. |
110 | | */ |
111 | | |
112 | | /* Potentially we can write different versions |
113 | | * of get_context and set_context for different |
114 | | * threading systems. |
115 | | * |
116 | | * This simple version relies on harfbuzz never |
117 | | * trying to make 2 allocations at once on |
118 | | * different threads. The only way that can happen |
119 | | * is when one of those other threads is someone |
120 | | * outside MuPDF calling harfbuzz while MuPDF |
121 | | * is running. This will cause us such huge |
122 | | * problems that for now, we'll just forbid it. |
123 | | */ |
124 | | |
125 | | static fz_context *fz_hb_secret = NULL; |
126 | | |
127 | | static void set_hb_context(fz_context *ctx) |
128 | 445k | { |
129 | 445k | fz_hb_secret = ctx; |
130 | 445k | } |
131 | | |
132 | | static fz_context *get_hb_context(void) |
133 | 1.73M | { |
134 | 1.73M | return fz_hb_secret; |
135 | 1.73M | } |
136 | | |
137 | | void fz_hb_lock(fz_context *ctx) |
138 | 222k | { |
139 | 222k | fz_ft_lock(ctx); |
140 | | |
141 | 222k | set_hb_context(ctx); |
142 | 222k | } |
143 | | |
144 | | void fz_hb_unlock(fz_context *ctx) |
145 | 222k | { |
146 | 222k | set_hb_context(NULL); |
147 | | |
148 | 222k | fz_ft_unlock(ctx); |
149 | 222k | } |
150 | | |
151 | | void *fz_hb_malloc(size_t size) |
152 | 1 | { |
153 | 1 | fz_context *ctx = get_hb_context(); |
154 | | |
155 | 1 | assert(ctx != NULL); |
156 | | |
157 | 1 | return Memento_label(fz_malloc_no_throw(ctx, size), "hb"); |
158 | 1 | } |
159 | | |
160 | | void *fz_hb_calloc(size_t n, size_t size) |
161 | 108k | { |
162 | 108k | fz_context *ctx = get_hb_context(); |
163 | | |
164 | 108k | assert(ctx != NULL); |
165 | | |
166 | 108k | return Memento_label(fz_calloc_no_throw(ctx, n, size), "hb"); |
167 | 108k | } |
168 | | |
169 | | void *fz_hb_realloc(void *ptr, size_t size) |
170 | 525k | { |
171 | 525k | fz_context *ctx = get_hb_context(); |
172 | | |
173 | 525k | assert(ctx != NULL); |
174 | | |
175 | 525k | return Memento_label(fz_realloc_no_throw(ctx, ptr, size), "hb"); |
176 | 525k | } |
177 | | |
178 | | void fz_hb_free(void *ptr) |
179 | 1.09M | { |
180 | 1.09M | fz_context *ctx = get_hb_context(); |
181 | | |
182 | 1.09M | assert(ctx != NULL); |
183 | | |
184 | 1.09M | fz_free(ctx, ptr); |
185 | 1.09M | } |