Line data Source code
1 : /*
2 : * Copyright © 2009 Red Hat, Inc.
3 : *
4 : * This is part of HarfBuzz, a text shaping library.
5 : *
6 : * Permission is hereby granted, without written agreement and without
7 : * license or royalty fees, to use, copy, modify, and distribute this
8 : * software and its documentation for any purpose, provided that the
9 : * above copyright notice and the following two paragraphs appear in
10 : * all copies of this software.
11 : *
12 : * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 : * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 : * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 : * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 : * DAMAGE.
17 : *
18 : * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 : * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 : * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 : * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 : * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 : *
24 : * Red Hat Author(s): Behdad Esfahbod
25 : */
26 :
27 : /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
28 : #ifndef _POSIX_C_SOURCE
29 : #define _POSIX_C_SOURCE 199309L
30 : #endif
31 :
32 : #include "hb-private.hh"
33 :
34 : #include "hb-object-private.hh"
35 :
36 : #ifdef HAVE_SYS_MMAN_H
37 : #ifdef HAVE_UNISTD_H
38 : #include <unistd.h>
39 : #endif /* HAVE_UNISTD_H */
40 : #include <sys/mman.h>
41 : #endif /* HAVE_SYS_MMAN_H */
42 :
43 : #include <stdio.h>
44 : #include <errno.h>
45 :
46 :
47 :
48 : #ifndef HB_DEBUG_BLOB
49 : #define HB_DEBUG_BLOB (HB_DEBUG+0)
50 : #endif
51 :
52 :
53 : struct hb_blob_t {
54 : hb_object_header_t header;
55 : ASSERT_POD ();
56 :
57 : bool immutable;
58 :
59 : const char *data;
60 : unsigned int length;
61 : hb_memory_mode_t mode;
62 :
63 : void *user_data;
64 : hb_destroy_func_t destroy;
65 : };
66 :
67 :
68 : static bool _try_writable (hb_blob_t *blob);
69 :
70 : static void
71 9 : _hb_blob_destroy_user_data (hb_blob_t *blob)
72 : {
73 9 : if (blob->destroy) {
74 9 : blob->destroy (blob->user_data);
75 9 : blob->user_data = NULL;
76 9 : blob->destroy = NULL;
77 : }
78 9 : }
79 :
80 : /**
81 : * hb_blob_create: (skip)
82 : * @data: Pointer to blob data.
83 : * @length: Length of @data in bytes.
84 : * @mode: Memory mode for @data.
85 : * @user_data: Data parameter to pass to @destroy.
86 : * @destroy: Callback to call when @data is not needed anymore.
87 : *
88 : * Creates a new "blob" object wrapping @data. The @mode parameter is used
89 : * to negotiate ownership and lifecycle of @data.
90 : *
91 : * Return value: New blob, or the empty blob if something failed or if @length is
92 : * zero. Destroy with hb_blob_destroy().
93 : *
94 : * Since: 0.9.2
95 : **/
96 : hb_blob_t *
97 14 : hb_blob_create (const char *data,
98 : unsigned int length,
99 : hb_memory_mode_t mode,
100 : void *user_data,
101 : hb_destroy_func_t destroy)
102 : {
103 : hb_blob_t *blob;
104 :
105 28 : if (!length ||
106 28 : length >= 1u << 31 ||
107 : !(blob = hb_object_create<hb_blob_t> ())) {
108 0 : if (destroy)
109 0 : destroy (user_data);
110 0 : return hb_blob_get_empty ();
111 : }
112 :
113 14 : blob->data = data;
114 14 : blob->length = length;
115 14 : blob->mode = mode;
116 :
117 14 : blob->user_data = user_data;
118 14 : blob->destroy = destroy;
119 :
120 14 : if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
121 0 : blob->mode = HB_MEMORY_MODE_READONLY;
122 0 : if (!_try_writable (blob)) {
123 0 : hb_blob_destroy (blob);
124 0 : return hb_blob_get_empty ();
125 : }
126 : }
127 :
128 14 : return blob;
129 : }
130 :
131 : /**
132 : * hb_blob_create_sub_blob:
133 : * @parent: Parent blob.
134 : * @offset: Start offset of sub-blob within @parent, in bytes.
135 : * @length: Length of sub-blob.
136 : *
137 : * Returns a blob that represents a range of bytes in @parent. The new
138 : * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it
139 : * will never modify data in the parent blob. The parent data is not
140 : * expected to be modified, and will result in undefined behavior if it
141 : * is.
142 : *
143 : * Makes @parent immutable.
144 : *
145 : * Return value: New blob, or the empty blob if something failed or if
146 : * @length is zero or @offset is beyond the end of @parent's data. Destroy
147 : * with hb_blob_destroy().
148 : *
149 : * Since: 0.9.2
150 : **/
151 : hb_blob_t *
152 0 : hb_blob_create_sub_blob (hb_blob_t *parent,
153 : unsigned int offset,
154 : unsigned int length)
155 : {
156 : hb_blob_t *blob;
157 :
158 0 : if (!length || offset >= parent->length)
159 0 : return hb_blob_get_empty ();
160 :
161 0 : hb_blob_make_immutable (parent);
162 :
163 0 : blob = hb_blob_create (parent->data + offset,
164 0 : MIN (length, parent->length - offset),
165 : HB_MEMORY_MODE_READONLY,
166 0 : hb_blob_reference (parent),
167 0 : (hb_destroy_func_t) hb_blob_destroy);
168 :
169 0 : return blob;
170 : }
171 :
172 : /**
173 : * hb_blob_get_empty:
174 : *
175 : * Returns the singleton empty blob.
176 : *
177 : * See TODO:link object types for more information.
178 : *
179 : * Return value: (transfer full): the empty blob.
180 : *
181 : * Since: 0.9.2
182 : **/
183 : hb_blob_t *
184 16 : hb_blob_get_empty (void)
185 : {
186 : static const hb_blob_t _hb_blob_nil = {
187 : HB_OBJECT_HEADER_STATIC,
188 :
189 : true, /* immutable */
190 :
191 : NULL, /* data */
192 : 0, /* length */
193 : HB_MEMORY_MODE_READONLY, /* mode */
194 :
195 : NULL, /* user_data */
196 : NULL /* destroy */
197 : };
198 :
199 16 : return const_cast<hb_blob_t *> (&_hb_blob_nil);
200 : }
201 :
202 : /**
203 : * hb_blob_reference: (skip)
204 : * @blob: a blob.
205 : *
206 : * Increases the reference count on @blob.
207 : *
208 : * See TODO:link object types for more information.
209 : *
210 : * Return value: @blob.
211 : *
212 : * Since: 0.9.2
213 : **/
214 : hb_blob_t *
215 15 : hb_blob_reference (hb_blob_t *blob)
216 : {
217 15 : return hb_object_reference (blob);
218 : }
219 :
220 : /**
221 : * hb_blob_destroy: (skip)
222 : * @blob: a blob.
223 : *
224 : * Descreases the reference count on @blob, and if it reaches zero, destroys
225 : * @blob, freeing all memory, possibly calling the destroy-callback the blob
226 : * was created for if it has not been called already.
227 : *
228 : * See TODO:link object types for more information.
229 : *
230 : * Since: 0.9.2
231 : **/
232 : void
233 30 : hb_blob_destroy (hb_blob_t *blob)
234 : {
235 30 : if (!hb_object_destroy (blob)) return;
236 :
237 9 : _hb_blob_destroy_user_data (blob);
238 :
239 9 : free (blob);
240 : }
241 :
242 : /**
243 : * hb_blob_set_user_data: (skip)
244 : * @blob: a blob.
245 : * @key: key for data to set.
246 : * @data: data to set.
247 : * @destroy: callback to call when @data is not needed anymore.
248 : * @replace: whether to replace an existing data with the same key.
249 : *
250 : * Return value:
251 : *
252 : * Since: 0.9.2
253 : **/
254 : hb_bool_t
255 0 : hb_blob_set_user_data (hb_blob_t *blob,
256 : hb_user_data_key_t *key,
257 : void * data,
258 : hb_destroy_func_t destroy,
259 : hb_bool_t replace)
260 : {
261 0 : return hb_object_set_user_data (blob, key, data, destroy, replace);
262 : }
263 :
264 : /**
265 : * hb_blob_get_user_data: (skip)
266 : * @blob: a blob.
267 : * @key: key for data to get.
268 : *
269 : *
270 : *
271 : * Return value: (transfer none):
272 : *
273 : * Since: 0.9.2
274 : **/
275 : void *
276 0 : hb_blob_get_user_data (hb_blob_t *blob,
277 : hb_user_data_key_t *key)
278 : {
279 0 : return hb_object_get_user_data (blob, key);
280 : }
281 :
282 :
283 : /**
284 : * hb_blob_make_immutable:
285 : * @blob: a blob.
286 : *
287 : *
288 : *
289 : * Since: 0.9.2
290 : **/
291 : void
292 14 : hb_blob_make_immutable (hb_blob_t *blob)
293 : {
294 14 : if (hb_object_is_inert (blob))
295 2 : return;
296 :
297 12 : blob->immutable = true;
298 : }
299 :
300 : /**
301 : * hb_blob_is_immutable:
302 : * @blob: a blob.
303 : *
304 : *
305 : *
306 : * Return value: TODO
307 : *
308 : * Since: 0.9.2
309 : **/
310 : hb_bool_t
311 0 : hb_blob_is_immutable (hb_blob_t *blob)
312 : {
313 0 : return blob->immutable;
314 : }
315 :
316 :
317 : /**
318 : * hb_blob_get_length:
319 : * @blob: a blob.
320 : *
321 : *
322 : *
323 : * Return value: the length of blob data in bytes.
324 : *
325 : * Since: 0.9.2
326 : **/
327 : unsigned int
328 26 : hb_blob_get_length (hb_blob_t *blob)
329 : {
330 26 : return blob->length;
331 : }
332 :
333 : /**
334 : * hb_blob_get_data:
335 : * @blob: a blob.
336 : * @length: (out):
337 : *
338 : *
339 : *
340 : * Returns: (transfer none) (array length=length):
341 : *
342 : * Since: 0.9.2
343 : **/
344 : const char *
345 30 : hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
346 : {
347 30 : if (length)
348 2 : *length = blob->length;
349 :
350 30 : return blob->data;
351 : }
352 :
353 : /**
354 : * hb_blob_get_data_writable:
355 : * @blob: a blob.
356 : * @length: (out): output length of the writable data.
357 : *
358 : * Tries to make blob data writable (possibly copying it) and
359 : * return pointer to data.
360 : *
361 : * Fails if blob has been made immutable, or if memory allocation
362 : * fails.
363 : *
364 : * Returns: (transfer none) (array length=length): Writable blob data,
365 : * or %NULL if failed.
366 : *
367 : * Since: 0.9.2
368 : **/
369 : char *
370 0 : hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
371 : {
372 0 : if (!_try_writable (blob)) {
373 0 : if (length)
374 0 : *length = 0;
375 :
376 0 : return NULL;
377 : }
378 :
379 0 : if (length)
380 0 : *length = blob->length;
381 :
382 0 : return const_cast<char *> (blob->data);
383 : }
384 :
385 :
386 : static hb_bool_t
387 0 : _try_make_writable_inplace_unix (hb_blob_t *blob)
388 : {
389 : #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
390 : uintptr_t pagesize = -1, mask, length;
391 : const char *addr;
392 :
393 : #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
394 : pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
395 : #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
396 : pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
397 : #elif defined(HAVE_GETPAGESIZE)
398 : pagesize = (uintptr_t) getpagesize ();
399 : #endif
400 :
401 : if ((uintptr_t) -1L == pagesize) {
402 : DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
403 : return false;
404 : }
405 : DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
406 :
407 : mask = ~(pagesize-1);
408 : addr = (const char *) (((uintptr_t) blob->data) & mask);
409 : length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask) - addr;
410 : DEBUG_MSG_FUNC (BLOB, blob,
411 : "calling mprotect on [%p..%p] (%lu bytes)",
412 : addr, addr+length, (unsigned long) length);
413 : if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
414 : DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
415 : return false;
416 : }
417 :
418 : blob->mode = HB_MEMORY_MODE_WRITABLE;
419 :
420 : DEBUG_MSG_FUNC (BLOB, blob,
421 : "successfully made [%p..%p] (%lu bytes) writable\n",
422 : addr, addr+length, (unsigned long) length);
423 : return true;
424 : #else
425 0 : return false;
426 : #endif
427 : }
428 :
429 : static bool
430 0 : _try_writable_inplace (hb_blob_t *blob)
431 : {
432 0 : DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
433 :
434 0 : if (_try_make_writable_inplace_unix (blob))
435 0 : return true;
436 :
437 0 : DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
438 :
439 : /* Failed to make writable inplace, mark that */
440 0 : blob->mode = HB_MEMORY_MODE_READONLY;
441 0 : return false;
442 : }
443 :
444 : static bool
445 0 : _try_writable (hb_blob_t *blob)
446 : {
447 0 : if (blob->immutable)
448 0 : return false;
449 :
450 0 : if (blob->mode == HB_MEMORY_MODE_WRITABLE)
451 0 : return true;
452 :
453 0 : if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
454 0 : return true;
455 :
456 0 : if (blob->mode == HB_MEMORY_MODE_WRITABLE)
457 0 : return true;
458 :
459 :
460 0 : DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
461 :
462 : char *new_data;
463 :
464 0 : new_data = (char *) malloc (blob->length);
465 0 : if (unlikely (!new_data))
466 0 : return false;
467 :
468 0 : DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
469 :
470 0 : memcpy (new_data, blob->data, blob->length);
471 0 : _hb_blob_destroy_user_data (blob);
472 0 : blob->mode = HB_MEMORY_MODE_WRITABLE;
473 0 : blob->data = new_data;
474 0 : blob->user_data = new_data;
475 0 : blob->destroy = free;
476 :
477 0 : return true;
478 : }
|