Imported Upstream version 0.9.3
[platform/upstream/harfbuzz.git] / src / hb-blob.cc
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 #include "hb-private.hh"
28
29 #include "hb-blob.h"
30 #include "hb-object-private.hh"
31
32 #ifdef HAVE_SYS_MMAN_H
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif /* HAVE_UNISTD_H */
36 #include <sys/mman.h>
37 #endif /* HAVE_SYS_MMAN_H */
38
39 #include <stdio.h>
40 #include <errno.h>
41
42
43
44 #ifndef HB_DEBUG_BLOB
45 #define HB_DEBUG_BLOB (HB_DEBUG+0)
46 #endif
47
48
49 struct hb_blob_t {
50   hb_object_header_t header;
51   ASSERT_POD ();
52
53   bool immutable;
54
55   const char *data;
56   unsigned int length;
57   hb_memory_mode_t mode;
58
59   void *user_data;
60   hb_destroy_func_t destroy;
61 };
62
63
64 static bool _try_writable (hb_blob_t *blob);
65
66 static void
67 _hb_blob_destroy_user_data (hb_blob_t *blob)
68 {
69   if (blob->destroy) {
70     blob->destroy (blob->user_data);
71     blob->user_data = NULL;
72     blob->destroy = NULL;
73   }
74 }
75
76 hb_blob_t *
77 hb_blob_create (const char        *data,
78                 unsigned int       length,
79                 hb_memory_mode_t   mode,
80                 void              *user_data,
81                 hb_destroy_func_t  destroy)
82 {
83   hb_blob_t *blob;
84
85   if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
86     if (destroy)
87       destroy (user_data);
88     return hb_blob_get_empty ();
89   }
90
91   blob->data = data;
92   blob->length = length;
93   blob->mode = mode;
94
95   blob->user_data = user_data;
96   blob->destroy = destroy;
97
98   if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
99     blob->mode = HB_MEMORY_MODE_READONLY;
100     if (!_try_writable (blob)) {
101       hb_blob_destroy (blob);
102       return hb_blob_get_empty ();
103     }
104   }
105
106   return blob;
107 }
108
109 hb_blob_t *
110 hb_blob_create_sub_blob (hb_blob_t    *parent,
111                          unsigned int  offset,
112                          unsigned int  length)
113 {
114   hb_blob_t *blob;
115
116   if (!length || offset >= parent->length)
117     return hb_blob_get_empty ();
118
119   hb_blob_make_immutable (parent);
120
121   blob = hb_blob_create (parent->data + offset,
122                          MIN (length, parent->length - offset),
123                          parent->mode,
124                          hb_blob_reference (parent),
125                          (hb_destroy_func_t) hb_blob_destroy);
126
127   return blob;
128 }
129
130 hb_blob_t *
131 hb_blob_get_empty (void)
132 {
133   static const hb_blob_t _hb_blob_nil = {
134     HB_OBJECT_HEADER_STATIC,
135
136     true, /* immutable */
137
138     NULL, /* data */
139     0, /* length */
140     HB_MEMORY_MODE_READONLY, /* mode */
141
142     NULL, /* user_data */
143     NULL  /* destroy */
144   };
145
146   return const_cast<hb_blob_t *> (&_hb_blob_nil);
147 }
148
149 hb_blob_t *
150 hb_blob_reference (hb_blob_t *blob)
151 {
152   return hb_object_reference (blob);
153 }
154
155 void
156 hb_blob_destroy (hb_blob_t *blob)
157 {
158   if (!hb_object_destroy (blob)) return;
159
160   _hb_blob_destroy_user_data (blob);
161
162   free (blob);
163 }
164
165 hb_bool_t
166 hb_blob_set_user_data (hb_blob_t          *blob,
167                        hb_user_data_key_t *key,
168                        void *              data,
169                        hb_destroy_func_t   destroy,
170                        hb_bool_t           replace)
171 {
172   return hb_object_set_user_data (blob, key, data, destroy, replace);
173 }
174
175 void *
176 hb_blob_get_user_data (hb_blob_t          *blob,
177                        hb_user_data_key_t *key)
178 {
179   return hb_object_get_user_data (blob, key);
180 }
181
182
183 void
184 hb_blob_make_immutable (hb_blob_t *blob)
185 {
186   if (hb_object_is_inert (blob))
187     return;
188
189   blob->immutable = true;
190 }
191
192 hb_bool_t
193 hb_blob_is_immutable (hb_blob_t *blob)
194 {
195   return blob->immutable;
196 }
197
198
199 unsigned int
200 hb_blob_get_length (hb_blob_t *blob)
201 {
202   return blob->length;
203 }
204
205 const char *
206 hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
207 {
208   if (length)
209     *length = blob->length;
210
211   return blob->data;
212 }
213
214 char *
215 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
216 {
217   if (!_try_writable (blob)) {
218     if (length)
219       *length = 0;
220
221     return NULL;
222   }
223
224   if (length)
225     *length = blob->length;
226
227   return const_cast<char *> (blob->data);
228 }
229
230
231 static hb_bool_t
232 _try_make_writable_inplace_unix (hb_blob_t *blob)
233 {
234 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
235   uintptr_t pagesize = -1, mask, length;
236   const char *addr;
237
238 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
239   pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
240 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
241   pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
242 #elif defined(HAVE_GETPAGESIZE)
243   pagesize = (uintptr_t) getpagesize ();
244 #endif
245
246   if ((uintptr_t) -1L == pagesize) {
247     DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
248     return false;
249   }
250   DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
251
252   mask = ~(pagesize-1);
253   addr = (const char *) (((uintptr_t) blob->data) & mask);
254   length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
255   DEBUG_MSG_FUNC (BLOB, blob,
256                   "calling mprotect on [%p..%p] (%lu bytes)",
257                   addr, addr+length, (unsigned long) length);
258   if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
259     DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
260     return false;
261   }
262
263   blob->mode = HB_MEMORY_MODE_WRITABLE;
264
265   DEBUG_MSG_FUNC (BLOB, blob,
266                   "successfully made [%p..%p] (%lu bytes) writable\n",
267                   addr, addr+length, (unsigned long) length);
268   return true;
269 #else
270   return false;
271 #endif
272 }
273
274 static bool
275 _try_writable_inplace (hb_blob_t *blob)
276 {
277   DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
278
279   if (_try_make_writable_inplace_unix (blob))
280     return true;
281
282   DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
283
284   /* Failed to make writable inplace, mark that */
285   blob->mode = HB_MEMORY_MODE_READONLY;
286   return false;
287 }
288
289 static bool
290 _try_writable (hb_blob_t *blob)
291 {
292   if (blob->immutable)
293     return false;
294
295   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
296     return true;
297
298   if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
299     return true;
300
301   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
302     return true;
303
304
305   DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
306
307   char *new_data;
308
309   new_data = (char *) malloc (blob->length);
310   if (unlikely (!new_data))
311     return false;
312
313   DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
314
315   memcpy (new_data, blob->data, blob->length);
316   _hb_blob_destroy_user_data (blob);
317   blob->mode = HB_MEMORY_MODE_WRITABLE;
318   blob->data = new_data;
319   blob->user_data = new_data;
320   blob->destroy = free;
321
322   return true;
323 }
324
325