mem.c 11.3 KB
Newer Older
1
/*
2
 * default memory allocator for libavutil
3
 * Copyright (c) 2002 Fabrice Bellard
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21

Michael Niedermayer's avatar
Michael Niedermayer committed
22
/**
23
 * @file
24
 * default memory allocator for libavutil
Michael Niedermayer's avatar
Michael Niedermayer committed
25
 */
26

27 28
#define _XOPEN_SOURCE 600

29
#include "config.h"
30

31
#include <limits.h>
32
#include <stdint.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#if HAVE_MALLOC_H
36 37 38
#include <malloc.h>
#endif

39
#include "avassert.h"
40
#include "avutil.h"
41
#include "common.h"
42
#include "intreadwrite.h"
43 44
#include "mem.h"

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#ifdef MALLOC_PREFIX

#define malloc         AV_JOIN(MALLOC_PREFIX, malloc)
#define memalign       AV_JOIN(MALLOC_PREFIX, memalign)
#define posix_memalign AV_JOIN(MALLOC_PREFIX, posix_memalign)
#define realloc        AV_JOIN(MALLOC_PREFIX, realloc)
#define free           AV_JOIN(MALLOC_PREFIX, free)

void *malloc(size_t size);
void *memalign(size_t align, size_t size);
int   posix_memalign(void **ptr, size_t align, size_t size);
void *realloc(void *ptr, size_t size);
void  free(void *ptr);

#endif /* MALLOC_PREFIX */

61 62
#define ALIGN (HAVE_AVX ? 32 : 16)

63 64 65 66
/* NOTE: if you want to override these functions with your own
 * implementations (not recommended) you have to link libav* as
 * dynamic libraries and remove -Wl,-Bsymbolic from the linker flags.
 * Note that this will cost performance. */
67

68 69 70 71 72
static size_t max_alloc_size= INT_MAX;

void av_max_alloc(size_t max){
    max_alloc_size = max;
}
73

74
void *av_malloc(size_t size)
75
{
76
    void *ptr = NULL;
77
#if CONFIG_MEMALIGN_HACK
78
    long diff;
79
#endif
80

81
    /* let's disallow possibly ambiguous cases */
82
    if (size > (max_alloc_size - 32))
83
        return NULL;
84

85
#if CONFIG_MEMALIGN_HACK
86
    ptr = malloc(size + ALIGN);
87
    if (!ptr)
88
        return ptr;
89
    diff              = ((~(long)ptr)&(ALIGN - 1)) + 1;
90 91
    ptr               = (char *)ptr + diff;
    ((char *)ptr)[-1] = diff;
92
#elif HAVE_POSIX_MEMALIGN
93
    if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation
94
    if (posix_memalign(&ptr, ALIGN, size))
95
        ptr = NULL;
96
#elif HAVE_ALIGNED_MALLOC
97
    ptr = _aligned_malloc(size, ALIGN);
98
#elif HAVE_MEMALIGN
99
#ifndef __DJGPP__
100
    ptr = memalign(ALIGN, size);
101 102 103
#else
    ptr = memalign(size, ALIGN);
#endif
104
    /* Why 64?
105 106 107 108 109 110 111
     * Indeed, we should align it:
     *   on  4 for 386
     *   on 16 for 486
     *   on 32 for 586, PPro - K6-III
     *   on 64 for K7 (maybe for P3 too).
     * Because L1 and L2 caches are aligned on those values.
     * But I don't want to code such logic here!
112
     */
113 114 115
    /* Why 32?
     * For AVX ASM. SSE / NEON needs only 16.
     * Why not larger? Because I did not see a difference in benchmarks ...
Michael Niedermayer's avatar
Michael Niedermayer committed
116
     */
117 118 119 120 121 122 123 124 125 126
    /* benchmarks with P3
     * memalign(64) + 1          3071, 3051, 3032
     * memalign(64) + 2          3051, 3032, 3041
     * memalign(64) + 4          2911, 2896, 2915
     * memalign(64) + 8          2545, 2554, 2550
     * memalign(64) + 16         2543, 2572, 2563
     * memalign(64) + 32         2546, 2545, 2571
     * memalign(64) + 64         2570, 2533, 2558
     *
     * BTW, malloc seems to do 8-byte alignment by default here.
Michael Niedermayer's avatar
Michael Niedermayer committed
127
     */
128 129 130
#else
    ptr = malloc(size);
#endif
131 132
    if(!ptr && !size) {
        size = 1;
133
        ptr= av_malloc(1);
134 135 136
    }
#if CONFIG_MEMORY_POISONING
    if (ptr)
137
        memset(ptr, FF_MEMORY_POISON, size);
138
#endif
139 140 141
    return ptr;
}

142
void *av_realloc(void *ptr, size_t size)
143
{
144
#if CONFIG_MEMALIGN_HACK
145 146
    int diff;
#endif
147

148
    /* let's disallow possibly ambiguous cases */
149
    if (size > (max_alloc_size - 32))
150 151
        return NULL;

152
#if CONFIG_MEMALIGN_HACK
153
    //FIXME this isn't aligned correctly, though it probably isn't needed
154 155 156
    if (!ptr)
        return av_malloc(size);
    diff = ((char *)ptr)[-1];
157
    av_assert0(diff>0 && diff<=ALIGN);
158 159 160
    ptr = realloc((char *)ptr - diff, size + diff);
    if (ptr)
        ptr = (char *)ptr + diff;
161
    return ptr;
162
#elif HAVE_ALIGNED_MALLOC
163
    return _aligned_realloc(ptr, size + !size, ALIGN);
164
#else
165
    return realloc(ptr, size + !size);
166
#endif
Michael Niedermayer's avatar
Michael Niedermayer committed
167 168
}

Nicolas George's avatar
Nicolas George committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
void *av_realloc_f(void *ptr, size_t nelem, size_t elsize)
{
    size_t size;
    void *r;

    if (av_size_mult(elsize, nelem, &size)) {
        av_free(ptr);
        return NULL;
    }
    r = av_realloc(ptr, size);
    if (!r && size)
        av_free(ptr);
    return r;
}

184 185 186 187 188
int av_reallocp(void *ptr, size_t size)
{
    void **ptrptr = ptr;
    void *ret;

189 190 191 192
    if (!size) {
        av_freep(ptr);
        return 0;
    }
193 194 195 196 197 198 199 200 201 202 203
    ret = av_realloc(*ptrptr, size);

    if (!ret) {
        av_freep(ptr);
        return AVERROR(ENOMEM);
    }

    *ptrptr = ret;
    return 0;
}

204 205
void *av_realloc_array(void *ptr, size_t nmemb, size_t size)
{
206
    if (!size || nmemb >= INT_MAX / size)
207 208 209 210 211 212 213
        return NULL;
    return av_realloc(ptr, nmemb * size);
}

int av_reallocp_array(void *ptr, size_t nmemb, size_t size)
{
    void **ptrptr = ptr;
214
    *ptrptr = av_realloc_f(*ptrptr, nmemb, size);
215
    if (!*ptrptr && nmemb && size)
216 217 218 219
        return AVERROR(ENOMEM);
    return 0;
}

220 221
void av_free(void *ptr)
{
222
#if CONFIG_MEMALIGN_HACK
223 224 225 226 227
    if (ptr) {
        int v= ((char *)ptr)[-1];
        av_assert0(v>0 && v<=ALIGN);
        free((char *)ptr - v);
    }
228 229
#elif HAVE_ALIGNED_MALLOC
    _aligned_free(ptr);
230
#else
231
    free(ptr);
232
#endif
233 234
}

235 236
void av_freep(void *arg)
{
237
    void **ptr = (void **)arg;
238 239 240 241
    av_free(*ptr);
    *ptr = NULL;
}

242
void *av_mallocz(size_t size)
243
{
244
    void *ptr = av_malloc(size);
245 246 247 248 249
    if (ptr)
        memset(ptr, 0, size);
    return ptr;
}

Laurent Aimar's avatar
Laurent Aimar committed
250 251 252 253 254 255 256
void *av_calloc(size_t nmemb, size_t size)
{
    if (size <= 0 || nmemb >= INT_MAX / size)
        return NULL;
    return av_mallocz(nmemb * size);
}

257 258
char *av_strdup(const char *s)
{
259 260
    char *ptr = NULL;
    if (s) {
Michael Niedermayer's avatar
Michael Niedermayer committed
261
        int len = strlen(s) + 1;
262
        ptr = av_realloc(NULL, len);
Michael Niedermayer's avatar
Michael Niedermayer committed
263 264
        if (ptr)
            memcpy(ptr, s, len);
265
    }
266 267 268
    return ptr;
}

269 270 271 272 273 274 275 276 277 278 279
void *av_memdup(const void *p, size_t size)
{
    void *ptr = NULL;
    if (p) {
        ptr = av_malloc(size);
        if (ptr)
            memcpy(ptr, p, size);
    }
    return ptr;
}

280 281 282 283 284 285 286 287 288
void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem)
{
    /* see similar ffmpeg.c:grow_array() */
    int nb, nb_alloc;
    intptr_t *tab;

    nb = *nb_ptr;
    tab = *(intptr_t**)tab_ptr;
    if ((nb & (nb - 1)) == 0) {
289
        if (nb == 0) {
290
            nb_alloc = 1;
291 292 293
        } else {
            if (nb > INT_MAX / (2 * sizeof(intptr_t)))
                goto fail;
294
            nb_alloc = nb * 2;
295
        }
296
        tab = av_realloc(tab, nb_alloc * sizeof(intptr_t));
297 298
        if (!tab)
            goto fail;
299 300 301 302
        *(intptr_t**)tab_ptr = tab;
    }
    tab[nb++] = (intptr_t)elem;
    *nb_ptr = nb;
303 304 305 306 307
    return;

fail:
    av_freep(tab_ptr);
    *nb_ptr = 0;
308
}
309

310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size,
                       const uint8_t *elem_data)
{
    int nb = *nb_ptr, nb_alloc;
    uint8_t *tab = *tab_ptr, *tab_elem_data;

    if ((nb & (nb - 1)) == 0) {
        if (nb == 0) {
            nb_alloc = 1;
        } else {
            if (nb > INT_MAX / (2 * elem_size))
                goto fail;
            nb_alloc = nb * 2;
        }
        tab = av_realloc(tab, nb_alloc * elem_size);
        if (!tab)
            goto fail;
        *tab_ptr = tab;
    }
    *nb_ptr = nb + 1;
    tab_elem_data = tab + nb*elem_size;
    if (elem_data)
        memcpy(tab_elem_data, elem_data, elem_size);
    else if (CONFIG_MEMORY_POISONING)
        memset(tab_elem_data, FF_MEMORY_POISON, elem_size);
    return tab_elem_data;

fail:
    av_freep(tab_ptr);
    *nb_ptr = 0;
    return NULL;
}

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
static void fill16(uint8_t *dst, int len)
{
    uint32_t v = AV_RN16(dst - 2);

    v |= v << 16;

    while (len >= 4) {
        AV_WN32(dst, v);
        dst += 4;
        len -= 4;
    }

    while (len--) {
        *dst = dst[-2];
        dst++;
    }
}

static void fill24(uint8_t *dst, int len)
{
#if HAVE_BIGENDIAN
    uint32_t v = AV_RB24(dst - 3);
    uint32_t a = v << 8  | v >> 16;
    uint32_t b = v << 16 | v >> 8;
    uint32_t c = v << 24 | v;
#else
    uint32_t v = AV_RL24(dst - 3);
    uint32_t a = v       | v << 24;
    uint32_t b = v >> 8  | v << 16;
    uint32_t c = v >> 16 | v << 8;
#endif

    while (len >= 12) {
        AV_WN32(dst,     a);
        AV_WN32(dst + 4, b);
        AV_WN32(dst + 8, c);
        dst += 12;
        len -= 12;
    }

    if (len >= 4) {
        AV_WN32(dst, a);
        dst += 4;
        len -= 4;
    }

    if (len >= 4) {
        AV_WN32(dst, b);
        dst += 4;
        len -= 4;
    }

    while (len--) {
        *dst = dst[-3];
        dst++;
    }
}

static void fill32(uint8_t *dst, int len)
{
    uint32_t v = AV_RN32(dst - 4);

    while (len >= 4) {
        AV_WN32(dst, v);
        dst += 4;
        len -= 4;
    }

    while (len--) {
        *dst = dst[-4];
        dst++;
    }
}

417 418 419
void av_memcpy_backptr(uint8_t *dst, int back, int cnt)
{
    const uint8_t *src = &dst[-back];
420 421 422
    if (!back)
        return;

423 424
    if (back == 1) {
        memset(dst, *src, cnt);
425 426 427 428 429 430
    } else if (back == 2) {
        fill16(dst, cnt);
    } else if (back == 3) {
        fill24(dst, cnt);
    } else if (back == 4) {
        fill32(dst, cnt);
431
    } else {
432
        if (cnt >= 16) {
433 434 435 436 437 438 439 440
            int blocklen = back;
            while (cnt > blocklen) {
                memcpy(dst, src, blocklen);
                dst       += blocklen;
                cnt       -= blocklen;
                blocklen <<= 1;
            }
            memcpy(dst, src, cnt);
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
            return;
        }
        if (cnt >= 8) {
            AV_COPY32U(dst,     src);
            AV_COPY32U(dst + 4, src + 4);
            src += 8;
            dst += 8;
            cnt -= 8;
        }
        if (cnt >= 4) {
            AV_COPY32U(dst, src);
            src += 4;
            dst += 4;
            cnt -= 4;
        }
        if (cnt >= 2) {
            AV_COPY16U(dst, src);
            src += 2;
            dst += 2;
            cnt -= 2;
461
        }
462 463
        if (cnt)
            *dst = *src;
464 465
    }
}
466

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size)
{
    if (min_size < *size)
        return ptr;

    min_size = FFMAX(17 * min_size / 16 + 32, min_size);

    ptr = av_realloc(ptr, min_size);
    /* we could set this to the unmodified min_size but this is safer
     * if the user lost the ptr and uses NULL now
     */
    if (!ptr)
        min_size = 0;

    *size = min_size;

    return ptr;
}

486
static inline int ff_fast_malloc(void *ptr, unsigned int *size, size_t min_size, int zero_realloc)
487 488 489
{
    void **p = ptr;
    if (min_size < *size)
490
        return 0;
491 492
    min_size = FFMAX(17 * min_size / 16 + 32, min_size);
    av_free(*p);
493
    *p = zero_realloc ? av_mallocz(min_size) : av_malloc(min_size);
494 495 496
    if (!*p)
        min_size = 0;
    *size = min_size;
497
    return 1;
498
}
499 500 501 502 503 504

void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size)
{
    ff_fast_malloc(ptr, size, min_size, 0);
}