1 /*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdarg.h>
19 #include <math.h>
20 #include <assert.h>
21
22 #include <libavutil/common.h>
23 #include <libavutil/error.h>
24
25 #include "mpv_talloc.h"
26 #include "misc/bstr.h"
27 #include "misc/ctype.h"
28 #include "common/common.h"
29 #include "osdep/strnlen.h"
30
31 #define appendf(ptr, ...) \
32 do {(*(ptr)) = talloc_asprintf_append_buffer(*(ptr), __VA_ARGS__);} while(0)
33
34 // Return a talloc'ed string formatted according to the format string in fmt.
35 // On error, return NULL.
36 // Valid formats:
37 // %H, %h: hour (%H is padded with 0 to two digits)
38 // %M: minutes from 00-59 (hours are subtracted)
39 // %m: total minutes (includes hours, unlike %M)
40 // %S: seconds from 00-59 (minutes and hours are subtracted)
41 // %s: total seconds (includes hours and minutes)
42 // %f: like %s, but as float
43 // %T: milliseconds (000-999)
mp_format_time_fmt(const char * fmt,double time)44 char *mp_format_time_fmt(const char *fmt, double time)
45 {
46 if (time == MP_NOPTS_VALUE)
47 return talloc_strdup(NULL, "unknown");
48 char *sign = time < 0 ? "-" : "";
49 time = time < 0 ? -time : time;
50 long long int itime = time;
51 long long int h, m, tm, s;
52 int ms = lrint((time - itime) * 1000);
53 if (ms >= 1000) {
54 ms -= 1000;
55 itime += 1;
56 }
57 s = itime;
58 tm = s / 60;
59 h = s / 3600;
60 s -= h * 3600;
61 m = s / 60;
62 s -= m * 60;
63 char *res = talloc_strdup(NULL, "");
64 while (*fmt) {
65 if (fmt[0] == '%') {
66 fmt++;
67 switch (fmt[0]) {
68 case 'h': appendf(&res, "%s%lld", sign, h); break;
69 case 'H': appendf(&res, "%s%02lld", sign, h); break;
70 case 'm': appendf(&res, "%s%lld", sign, tm); break;
71 case 'M': appendf(&res, "%02lld", m); break;
72 case 's': appendf(&res, "%s%lld", sign, itime); break;
73 case 'S': appendf(&res, "%02lld", s); break;
74 case 'T': appendf(&res, "%03d", ms); break;
75 case 'f': appendf(&res, "%f", time); break;
76 case '%': appendf(&res, "%s", "%"); break;
77 default: goto error;
78 }
79 fmt++;
80 } else {
81 appendf(&res, "%c", *fmt);
82 fmt++;
83 }
84 }
85 return res;
86 error:
87 talloc_free(res);
88 return NULL;
89 }
90
mp_format_time(double time,bool fractions)91 char *mp_format_time(double time, bool fractions)
92 {
93 return mp_format_time_fmt(fractions ? "%H:%M:%S.%T" : "%H:%M:%S", time);
94 }
95
96 // Set rc to the union of rc and rc2
mp_rect_union(struct mp_rect * rc,const struct mp_rect * rc2)97 void mp_rect_union(struct mp_rect *rc, const struct mp_rect *rc2)
98 {
99 rc->x0 = MPMIN(rc->x0, rc2->x0);
100 rc->y0 = MPMIN(rc->y0, rc2->y0);
101 rc->x1 = MPMAX(rc->x1, rc2->x1);
102 rc->y1 = MPMAX(rc->y1, rc2->y1);
103 }
104
105 // Returns whether or not a point is contained by rc
mp_rect_contains(struct mp_rect * rc,int x,int y)106 bool mp_rect_contains(struct mp_rect *rc, int x, int y)
107 {
108 return rc->x0 <= x && x < rc->x1 && rc->y0 <= y && y < rc->y1;
109 }
110
111 // Set rc to the intersection of rc and src.
112 // Return false if the result is empty.
mp_rect_intersection(struct mp_rect * rc,const struct mp_rect * rc2)113 bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2)
114 {
115 rc->x0 = MPMAX(rc->x0, rc2->x0);
116 rc->y0 = MPMAX(rc->y0, rc2->y0);
117 rc->x1 = MPMIN(rc->x1, rc2->x1);
118 rc->y1 = MPMIN(rc->y1, rc2->y1);
119
120 return rc->x1 > rc->x0 && rc->y1 > rc->y0;
121 }
122
mp_rect_equals(struct mp_rect * rc1,struct mp_rect * rc2)123 bool mp_rect_equals(struct mp_rect *rc1, struct mp_rect *rc2)
124 {
125 return rc1->x0 == rc2->x0 && rc1->y0 == rc2->y0 &&
126 rc1->x1 == rc2->x1 && rc1->y1 == rc2->y1;
127 }
128
129 // Compute rc1-rc2, put result in res_array, return number of rectangles in
130 // res_array. In the worst case, there are 4 rectangles, so res_array must
131 // provide that much storage space.
mp_rect_subtract(const struct mp_rect * rc1,const struct mp_rect * rc2,struct mp_rect res[4])132 int mp_rect_subtract(const struct mp_rect *rc1, const struct mp_rect *rc2,
133 struct mp_rect res[4])
134 {
135 struct mp_rect rc = *rc1;
136 if (!mp_rect_intersection(&rc, rc2))
137 return 0;
138
139 int cnt = 0;
140 if (rc1->y0 < rc.y0)
141 res[cnt++] = (struct mp_rect){rc1->x0, rc1->y0, rc1->x1, rc.y0};
142 if (rc1->x0 < rc.x0)
143 res[cnt++] = (struct mp_rect){rc1->x0, rc.y0, rc.x0, rc.y1};
144 if (rc1->x1 > rc.x1)
145 res[cnt++] = (struct mp_rect){rc.x1, rc.y0, rc1->x1, rc.y1};
146 if (rc1->y1 > rc.y1)
147 res[cnt++] = (struct mp_rect){rc1->x0, rc.y1, rc1->x1, rc1->y1};
148 return cnt;
149 }
150
151 // This works like snprintf(), except that it starts writing the first output
152 // character to str[strlen(str)]. This returns the number of characters the
153 // string would have *appended* assuming a large enough buffer, will make sure
154 // str is null-terminated, and will never write to str[size] or past.
155 // Example:
156 // int example(char *buf, size_t buf_size, double num, char *str) {
157 // int n = 0;
158 // n += mp_snprintf_cat(buf, size, "%f", num);
159 // n += mp_snprintf_cat(buf, size, "%s", str);
160 // return n; }
161 // Note how this can be chained with functions similar in style.
mp_snprintf_cat(char * str,size_t size,const char * format,...)162 int mp_snprintf_cat(char *str, size_t size, const char *format, ...)
163 {
164 size_t len = strnlen(str, size);
165 assert(!size || len < size); // str with no 0-termination is not allowed
166 int r;
167 va_list ap;
168 va_start(ap, format);
169 r = vsnprintf(str + len, size - len, format, ap);
170 va_end(ap);
171 return r;
172 }
173
174 // Encode the unicode codepoint as UTF-8, and append to the end of the
175 // talloc'ed buffer. All guarantees bstr_xappend() give applies, such as
176 // implicit \0-termination for convenience.
mp_append_utf8_bstr(void * talloc_ctx,struct bstr * buf,uint32_t codepoint)177 void mp_append_utf8_bstr(void *talloc_ctx, struct bstr *buf, uint32_t codepoint)
178 {
179 char data[8];
180 uint8_t tmp;
181 char *output = data;
182 PUT_UTF8(codepoint, tmp, *output++ = tmp;);
183 bstr_xappend(talloc_ctx, buf, (bstr){data, output - data});
184 }
185
186 // Parse a C/JSON-style escape beginning at code, and append the result to *str
187 // using talloc. The input string (*code) must point to the first character
188 // after the initial '\', and after parsing *code is set to the first character
189 // after the current escape.
190 // On error, false is returned, and all input remains unchanged.
mp_parse_escape(void * talloc_ctx,bstr * dst,bstr * code)191 static bool mp_parse_escape(void *talloc_ctx, bstr *dst, bstr *code)
192 {
193 if (code->len < 1)
194 return false;
195 char replace = 0;
196 switch (code->start[0]) {
197 case '"': replace = '"'; break;
198 case '\\': replace = '\\'; break;
199 case '/': replace = '/'; break;
200 case 'b': replace = '\b'; break;
201 case 'f': replace = '\f'; break;
202 case 'n': replace = '\n'; break;
203 case 'r': replace = '\r'; break;
204 case 't': replace = '\t'; break;
205 case 'e': replace = '\x1b'; break;
206 case '\'': replace = '\''; break;
207 }
208 if (replace) {
209 bstr_xappend(talloc_ctx, dst, (bstr){&replace, 1});
210 *code = bstr_cut(*code, 1);
211 return true;
212 }
213 if (code->start[0] == 'x' && code->len >= 3) {
214 bstr num = bstr_splice(*code, 1, 3);
215 char c = bstrtoll(num, &num, 16);
216 if (num.len)
217 return false;
218 bstr_xappend(talloc_ctx, dst, (bstr){&c, 1});
219 *code = bstr_cut(*code, 3);
220 return true;
221 }
222 if (code->start[0] == 'u' && code->len >= 5) {
223 bstr num = bstr_splice(*code, 1, 5);
224 uint32_t c = bstrtoll(num, &num, 16);
225 if (num.len)
226 return false;
227 if (c >= 0xd800 && c <= 0xdbff) {
228 if (code->len < 5 + 6 // udddd + \udddd
229 || code->start[5] != '\\' || code->start[6] != 'u')
230 return false;
231 *code = bstr_cut(*code, 5 + 1);
232 bstr num2 = bstr_splice(*code, 1, 5);
233 uint32_t c2 = bstrtoll(num2, &num2, 16);
234 if (num2.len || c2 < 0xdc00 || c2 > 0xdfff)
235 return false;
236 c = ((c - 0xd800) << 10) + 0x10000 + (c2 - 0xdc00);
237 }
238 mp_append_utf8_bstr(talloc_ctx, dst, c);
239 *code = bstr_cut(*code, 5);
240 return true;
241 }
242 return false;
243 }
244
245 // Like mp_append_escaped_string, but set *dst to sliced *src if no escape
246 // sequences have to be parsed (i.e. no memory allocation is required), and
247 // if dst->start was NULL on function entry.
mp_append_escaped_string_noalloc(void * talloc_ctx,bstr * dst,bstr * src)248 bool mp_append_escaped_string_noalloc(void *talloc_ctx, bstr *dst, bstr *src)
249 {
250 bstr t = *src;
251 int cur = 0;
252 while (1) {
253 if (cur >= t.len || t.start[cur] == '"') {
254 *src = bstr_cut(t, cur);
255 t = bstr_splice(t, 0, cur);
256 if (dst->start == NULL) {
257 *dst = t;
258 } else {
259 bstr_xappend(talloc_ctx, dst, t);
260 }
261 return true;
262 } else if (t.start[cur] == '\\') {
263 bstr_xappend(talloc_ctx, dst, bstr_splice(t, 0, cur));
264 t = bstr_cut(t, cur + 1);
265 cur = 0;
266 if (!mp_parse_escape(talloc_ctx, dst, &t))
267 goto error;
268 } else {
269 cur++;
270 }
271 }
272 error:
273 return false;
274 }
275
276 // src is expected to point to a C-style string literal, *src pointing to the
277 // first char after the starting '"'. It will append the contents of the literal
278 // to *dst (using talloc_ctx) until the first '"' or the end of *str is found.
279 // See bstr_xappend() how data is appended to *dst.
280 // On success, *src will either start with '"', or be empty.
281 // On error, return false, and *dst will contain the string until the first
282 // error, *src is not changed.
283 // Note that dst->start will be implicitly \0-terminated on successful return,
284 // and if it was NULL or \0-terminated before calling the function.
285 // As mentioned above, the caller is responsible for skipping the '"' chars.
mp_append_escaped_string(void * talloc_ctx,bstr * dst,bstr * src)286 bool mp_append_escaped_string(void *talloc_ctx, bstr *dst, bstr *src)
287 {
288 if (mp_append_escaped_string_noalloc(talloc_ctx, dst, src)) {
289 // Guarantee copy (or allocation).
290 if (!dst->start || dst->start == src->start) {
291 bstr res = *dst;
292 *dst = (bstr){0};
293 bstr_xappend(talloc_ctx, dst, res);
294 }
295 return true;
296 }
297 return false;
298 }
299
300 // Behaves like strerror()/strerror_r(), but is thread- and GNU-safe.
mp_strerror_buf(char * buf,size_t buf_size,int errnum)301 char *mp_strerror_buf(char *buf, size_t buf_size, int errnum)
302 {
303 // This handles the nasty details of calling the right function for us.
304 av_strerror(AVERROR(errnum), buf, buf_size);
305 return buf;
306 }
307
mp_tag_str_buf(char * buf,size_t buf_size,uint32_t tag)308 char *mp_tag_str_buf(char *buf, size_t buf_size, uint32_t tag)
309 {
310 if (buf_size < 1)
311 return buf;
312 buf[0] = '\0';
313 for (int n = 0; n < 4; n++) {
314 uint8_t val = (tag >> (n * 8)) & 0xFF;
315 if (mp_isalnum(val) || val == '_' || val == ' ') {
316 mp_snprintf_cat(buf, buf_size, "%c", val);
317 } else {
318 mp_snprintf_cat(buf, buf_size, "[%d]", val);
319 }
320 }
321 return buf;
322 }
323
mp_tprintf_buf(char * buf,size_t buf_size,const char * format,...)324 char *mp_tprintf_buf(char *buf, size_t buf_size, const char *format, ...)
325 {
326 va_list ap;
327 va_start(ap, format);
328 vsnprintf(buf, buf_size, format, ap);
329 va_end(ap);
330 return buf;
331 }
332
mp_dup_str_array(void * tctx,char ** s)333 char **mp_dup_str_array(void *tctx, char **s)
334 {
335 char **r = NULL;
336 int num_r = 0;
337 for (int n = 0; s && s[n]; n++)
338 MP_TARRAY_APPEND(tctx, r, num_r, talloc_strdup(tctx, s[n]));
339 if (r)
340 MP_TARRAY_APPEND(tctx, r, num_r, NULL);
341 return r;
342 }
343
344 // Return rounded down integer log 2 of v, i.e. position of highest set bit.
345 // mp_log2(0) == 0
346 // mp_log2(1) == 0
347 // mp_log2(31) == 4
348 // mp_log2(32) == 5
mp_log2(uint32_t v)349 unsigned int mp_log2(uint32_t v)
350 {
351 #if defined(__GNUC__) && __GNUC__ >= 4
352 return v ? 31 - __builtin_clz(v) : 0;
353 #else
354 for (int x = 31; x >= 0; x--) {
355 if (v & (((uint32_t)1) << x))
356 return x;
357 }
358 return 0;
359 #endif
360 }
361
362 // If a power of 2, return it, otherwise return the next highest one, or 0.
363 // mp_round_next_power_of_2(65) == 128
364 // mp_round_next_power_of_2(64) == 64
365 // mp_round_next_power_of_2(0) == 1
366 // mp_round_next_power_of_2(UINT32_MAX) == 0
mp_round_next_power_of_2(uint32_t v)367 uint32_t mp_round_next_power_of_2(uint32_t v)
368 {
369 if (!v)
370 return 1;
371 if (!(v & (v - 1)))
372 return v;
373 int l = mp_log2(v) + 1;
374 return l == 32 ? 0 : (uint32_t)1 << l;
375 }
376