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