/src/libass/libass/ass_utils.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> |
3 | | * |
4 | | * This file is part of libass. |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include "config.h" |
20 | | #include "ass_compat.h" |
21 | | |
22 | | #include <stddef.h> |
23 | | #include <stdlib.h> |
24 | | #include <stdio.h> |
25 | | #include <stdint.h> |
26 | | #include <inttypes.h> |
27 | | |
28 | | #include "ass_library.h" |
29 | | #include "ass.h" |
30 | | #include "ass_utils.h" |
31 | | #include "ass_string.h" |
32 | | |
33 | | // Fallbacks |
34 | | #ifndef HAVE_STRDUP |
35 | | char *ass_strdup_fallback(const char *str) |
36 | | { |
37 | | size_t len = strlen(str) + 1; |
38 | | char *new_str = malloc(len); |
39 | | if (new_str) |
40 | | memcpy(new_str, str, len); |
41 | | return new_str; |
42 | | } |
43 | | #endif |
44 | | |
45 | | #ifndef HAVE_STRNDUP |
46 | | char *ass_strndup_fallback(const char *s, size_t n) |
47 | | { |
48 | | char *end = memchr(s, 0, n); |
49 | | size_t len = end ? end - s : n; |
50 | | char *new = len < SIZE_MAX ? malloc(len + 1) : NULL; |
51 | | if (new) { |
52 | | memcpy(new, s, len); |
53 | | new[len] = 0; |
54 | | } |
55 | | return new; |
56 | | } |
57 | | #endif |
58 | | |
59 | | void *ass_aligned_alloc(size_t alignment, size_t size, bool zero) |
60 | 36.3k | { |
61 | 36.3k | assert(!(alignment & (alignment - 1))); // alignment must be power of 2 |
62 | 36.3k | if (size >= SIZE_MAX - alignment - sizeof(void *)) |
63 | 0 | return NULL; |
64 | 36.3k | char *allocation = zero ? calloc(1, size + sizeof(void *) + alignment - 1) |
65 | 36.3k | : malloc(size + sizeof(void *) + alignment - 1); |
66 | 36.3k | if (!allocation) |
67 | 0 | return NULL; |
68 | 36.3k | char *ptr = allocation + sizeof(void *); |
69 | 36.3k | unsigned int misalign = (uintptr_t)ptr & (alignment - 1); |
70 | 36.3k | if (misalign) |
71 | 36.3k | ptr += alignment - misalign; |
72 | 36.3k | *((void **)ptr - 1) = allocation; |
73 | 36.3k | return ptr; |
74 | 36.3k | } |
75 | | |
76 | | void ass_aligned_free(void *ptr) |
77 | 264k | { |
78 | 264k | if (ptr) |
79 | 36.3k | free(*((void **)ptr - 1)); |
80 | 264k | } |
81 | | |
82 | | /** |
83 | | * This works similar to realloc(ptr, nmemb * size), but checks for overflow. |
84 | | * |
85 | | * Unlike some implementations of realloc, this never acts as a call to free(). |
86 | | * If the total size is 0, it is bumped up to 1. This means a NULL return always |
87 | | * means allocation failure, and the unportable realloc(0, 0) case is avoided. |
88 | | */ |
89 | | void *ass_realloc_array(void *ptr, size_t nmemb, size_t size) |
90 | 65.5k | { |
91 | 65.5k | if (nmemb > (SIZE_MAX / size)) |
92 | 0 | return NULL; |
93 | 65.5k | size *= nmemb; |
94 | 65.5k | if (size < 1) |
95 | 0 | size = 1; |
96 | | |
97 | 65.5k | return realloc(ptr, size); |
98 | 65.5k | } |
99 | | |
100 | | /** |
101 | | * Like ass_realloc_array(), but: |
102 | | * 1. on failure, return the original ptr value, instead of NULL |
103 | | * 2. set errno to indicate failure (errno!=0) or success (errno==0) |
104 | | */ |
105 | | void *ass_try_realloc_array(void *ptr, size_t nmemb, size_t size) |
106 | 45.1k | { |
107 | 45.1k | void *new_ptr = ass_realloc_array(ptr, nmemb, size); |
108 | 45.1k | if (new_ptr) { |
109 | 45.1k | errno = 0; |
110 | 45.1k | return new_ptr; |
111 | 45.1k | } else { |
112 | 0 | errno = ENOMEM; |
113 | 0 | return ptr; |
114 | 0 | } |
115 | 45.1k | } |
116 | | |
117 | | void ass_msg(ASS_Library *priv, int lvl, const char *fmt, ...) |
118 | 341k | { |
119 | 341k | va_list va; |
120 | 341k | va_start(va, fmt); |
121 | 341k | priv->msg_callback(lvl, fmt, va, priv->msg_callback_data); |
122 | 341k | va_end(va); |
123 | 341k | } |
124 | | |
125 | | unsigned ass_utf8_get_char(char **str) |
126 | 557k | { |
127 | 557k | uint8_t *strp = (uint8_t *) * str; |
128 | 557k | unsigned c = *strp++; |
129 | 557k | unsigned mask = 0x80; |
130 | 557k | int len = -1; |
131 | 803k | while (c & mask) { |
132 | 246k | mask >>= 1; |
133 | 246k | len++; |
134 | 246k | } |
135 | 557k | if (len <= 0 || len > 4) |
136 | 535k | goto no_utf8; |
137 | 22.0k | c &= mask - 1; |
138 | 31.7k | while ((*strp & 0xc0) == 0x80) { |
139 | 10.9k | if (len-- <= 0) |
140 | 1.26k | goto no_utf8; |
141 | 9.72k | c = (c << 6) | (*strp++ & 0x3f); |
142 | 9.72k | } |
143 | 20.7k | if (len) |
144 | 18.9k | goto no_utf8; |
145 | 1.78k | *str = (char *) strp; |
146 | 1.78k | return c; |
147 | | |
148 | 555k | no_utf8: |
149 | 555k | strp = (uint8_t *) * str; |
150 | 555k | c = *strp++; |
151 | 555k | *str = (char *) strp; |
152 | 555k | return c; |
153 | 20.7k | } |
154 | | |
155 | | /** |
156 | | * Original version from http://www.cprogramming.com/tutorial/utf8.c |
157 | | * \brief Converts a single UTF-32 code point to UTF-8 |
158 | | * \param dest Buffer to write to. Writes a NULL terminator. |
159 | | * \param ch 32-bit character code to convert |
160 | | * \return number of bytes written |
161 | | * converts a single character and ASSUMES YOU HAVE ENOUGH SPACE |
162 | | */ |
163 | | unsigned ass_utf8_put_char(char *dest, uint32_t ch) |
164 | 0 | { |
165 | 0 | char *orig_dest = dest; |
166 | |
|
167 | 0 | if (ch < 0x80) { |
168 | 0 | *dest++ = (char)ch; |
169 | 0 | } else if (ch < 0x800) { |
170 | 0 | *dest++ = (ch >> 6) | 0xC0; |
171 | 0 | *dest++ = (ch & 0x3F) | 0x80; |
172 | 0 | } else if (ch < 0x10000) { |
173 | 0 | *dest++ = (ch >> 12) | 0xE0; |
174 | 0 | *dest++ = ((ch >> 6) & 0x3F) | 0x80; |
175 | 0 | *dest++ = (ch & 0x3F) | 0x80; |
176 | 0 | } else if (ch < 0x110000) { |
177 | 0 | *dest++ = (ch >> 18) | 0xF0; |
178 | 0 | *dest++ = ((ch >> 12) & 0x3F) | 0x80; |
179 | 0 | *dest++ = ((ch >> 6) & 0x3F) | 0x80; |
180 | 0 | *dest++ = (ch & 0x3F) | 0x80; |
181 | 0 | } |
182 | |
|
183 | 0 | *dest = '\0'; |
184 | 0 | return dest - orig_dest; |
185 | 0 | } |
186 | | |
187 | | /** |
188 | | * \brief Parse UTF-16 and return the code point of the sequence starting at src. |
189 | | * \param src pointer to a pointer to the start of the UTF-16 data |
190 | | * (will be set to the start of the next code point) |
191 | | * \return the code point |
192 | | */ |
193 | | static uint32_t ass_read_utf16be(uint8_t **src, size_t bytes) |
194 | 0 | { |
195 | 0 | if (bytes < 2) |
196 | 0 | goto too_short; |
197 | | |
198 | 0 | uint32_t cp = ((*src)[0] << 8) | (*src)[1]; |
199 | 0 | *src += 2; |
200 | 0 | bytes -= 2; |
201 | |
|
202 | 0 | if (cp >= 0xD800 && cp <= 0xDBFF) { |
203 | 0 | if (bytes < 2) |
204 | 0 | goto too_short; |
205 | | |
206 | 0 | uint32_t cp2 = ((*src)[0] << 8) | (*src)[1]; |
207 | |
|
208 | 0 | if (cp2 < 0xDC00 || cp2 > 0xDFFF) |
209 | 0 | return 0xFFFD; |
210 | | |
211 | 0 | *src += 2; |
212 | |
|
213 | 0 | cp = 0x10000 + ((cp - 0xD800) << 10) + (cp2 - 0xDC00); |
214 | 0 | } |
215 | | |
216 | 0 | if (cp >= 0xDC00 && cp <= 0xDFFF) |
217 | 0 | return 0xFFFD; |
218 | | |
219 | 0 | return cp; |
220 | | |
221 | 0 | too_short: |
222 | 0 | *src += bytes; |
223 | 0 | return 0xFFFD; |
224 | 0 | } |
225 | | |
226 | | void ass_utf16be_to_utf8(char *dst, size_t dst_size, uint8_t *src, size_t src_size) |
227 | 0 | { |
228 | 0 | uint8_t *end = src + src_size; |
229 | |
|
230 | 0 | if (!dst_size) |
231 | 0 | return; |
232 | | |
233 | 0 | while (src < end) { |
234 | 0 | uint32_t cp = ass_read_utf16be(&src, end - src); |
235 | 0 | if (dst_size < 5) |
236 | 0 | break; |
237 | 0 | unsigned s = ass_utf8_put_char(dst, cp); |
238 | 0 | dst += s; |
239 | 0 | dst_size -= s; |
240 | 0 | } |
241 | |
|
242 | 0 | *dst = '\0'; |
243 | 0 | } |
244 | | |
245 | | /** |
246 | | * \brief find style by name the common way (\r matches differently) |
247 | | * \param track track |
248 | | * \param name style name |
249 | | * \return index in track->styles |
250 | | * Returns 0 if no styles found => expects at least 1 style. |
251 | | * Parsing code always adds "Default" style in the beginning. |
252 | | */ |
253 | | int ass_lookup_style(ASS_Track *track, char *name) |
254 | 17.6k | { |
255 | 17.6k | int i; |
256 | | // '*' seem to mean literally nothing; |
257 | | // VSFilter removes them as soon as it can |
258 | 17.7k | while (*name == '*') |
259 | 195 | ++name; |
260 | | // VSFilter then normalizes the case of "Default" |
261 | | // (only in contexts where this function is called) |
262 | 17.6k | if (ass_strcasecmp(name, "Default") == 0) |
263 | 194 | name = "Default"; |
264 | 39.0k | for (i = track->n_styles - 1; i >= 0; --i) { |
265 | 21.6k | if (strcmp(track->styles[i].Name, name) == 0) |
266 | 194 | return i; |
267 | 21.6k | } |
268 | 17.4k | i = track->default_style; |
269 | 17.4k | ass_msg(track->library, MSGL_WARN, |
270 | 17.4k | "[%p]: Warning: no style named '%s' found, using '%s'", |
271 | 17.4k | track, name, track->styles[i].Name); |
272 | 17.4k | return i; |
273 | 17.6k | } |