X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavutil%2Fmem.c;h=b7bb65c139de522b29647617845d272cf7cc353b;hb=32125781487411ed3b1b28b32063d6cd4024d4fc;hp=3eaa366d53567d88154b852676d761e5c209663c;hpb=fdf35f265ac239d8a28d1be58d96a82b219c88b7;p=ffmpeg diff --git a/libavutil/mem.c b/libavutil/mem.c index 3eaa366d535..b7bb65c139d 100644 --- a/libavutil/mem.c +++ b/libavutil/mem.c @@ -1,89 +1,112 @@ /* * default memory allocator for libavutil - * Copyright (c) 2002 Fabrice Bellard. + * Copyright (c) 2002 Fabrice Bellard * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * Libav is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * FFmpeg is distributed in the hope that it will be useful, + * Libav is distributed in the hope that it will be useful, * 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 - * License along with FFmpeg; if not, write to the Free Software + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** - * @file mem.c - * default memory allocator for libavutil. + * @file + * default memory allocator for libavutil */ -#include "common.h" - -/* here we can use OS dependent allocation functions */ -#undef malloc -#undef free -#undef realloc +#include "config.h" -#ifdef HAVE_MALLOC_H +#include +#include +#include +#include +#if HAVE_MALLOC_H #include #endif -/* you can redefine av_malloc and av_free in your project to use your - memory allocator. You do not need to suppress this file because the - linker will do it automatically */ +#include "avutil.h" +#include "common.h" +#include "intreadwrite.h" +#include "mem.h" + +#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 *av_malloc(unsigned int size) +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 */ + +/* You can redefine av_malloc and av_free in your project to use your + * memory allocator. You do not need to suppress this file because the + * linker will do it automatically. */ + +void *av_malloc(size_t size) { - void *ptr; -#ifdef CONFIG_MEMALIGN_HACK + void *ptr = NULL; +#if CONFIG_MEMALIGN_HACK long diff; #endif - /* let's disallow possible ambiguous cases */ - if(size > (INT_MAX-16) ) + /* let's disallow possibly ambiguous cases */ + if (size > (INT_MAX - 32) || !size) return NULL; -#ifdef CONFIG_MEMALIGN_HACK - ptr = malloc(size+16); - if(!ptr) +#if CONFIG_MEMALIGN_HACK + ptr = malloc(size + 32); + if (!ptr) return ptr; - diff= ((-(long)ptr - 1)&15) + 1; - ptr = (char*)ptr + diff; - ((char*)ptr)[-1]= diff; -#elif defined (HAVE_MEMALIGN) - ptr = memalign(16,size); + diff = ((-(long)ptr - 1) & 31) + 1; + ptr = (char *)ptr + diff; + ((char *)ptr)[-1] = diff; +#elif HAVE_POSIX_MEMALIGN + if (posix_memalign(&ptr, 32, size)) + ptr = NULL; +#elif HAVE_ALIGNED_MALLOC + ptr = _aligned_malloc(size, 32); +#elif HAVE_MEMALIGN + ptr = memalign(32, size); /* Why 64? - 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! + * 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! */ - /* Why 16? - Because some CPUs need alignment, for example SSE2 on P4, & most RISC CPUs - it will just trigger an exception and the unaligned load will be done in the - exception handler or it will just segfault (SSE2 on P4) - Why not larger? Because I did not see a difference in benchmarks ... + /* Why 32? + * For AVX ASM. SSE / NEON needs only 16. + * Why not larger? Because I did not see a difference in benchmarks ... */ - /* 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 + /* 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. */ #else ptr = malloc(size); @@ -91,45 +114,95 @@ void *av_malloc(unsigned int size) return ptr; } -void *av_realloc(void *ptr, unsigned int size) +void *av_realloc(void *ptr, size_t size) { -#ifdef CONFIG_MEMALIGN_HACK +#if CONFIG_MEMALIGN_HACK int diff; #endif - /* let's disallow possible ambiguous cases */ - if(size > (INT_MAX-16) ) + /* let's disallow possibly ambiguous cases */ + if (size > (INT_MAX - 16)) return NULL; -#ifdef CONFIG_MEMALIGN_HACK +#if CONFIG_MEMALIGN_HACK //FIXME this isn't aligned correctly, though it probably isn't needed - if(!ptr) return av_malloc(size); - diff= ((char*)ptr)[-1]; - return (char*)realloc((char*)ptr - diff, size + diff) + diff; + if (!ptr) + return av_malloc(size); + diff = ((char *)ptr)[-1]; + return (char *)realloc((char *)ptr - diff, size + diff) + diff; +#elif HAVE_ALIGNED_MALLOC + return _aligned_realloc(ptr, size, 32); #else return realloc(ptr, size); #endif } +int av_reallocp(void *ptr, size_t size) +{ + void **ptrptr = ptr; + void *ret; + + if (!size) { + av_freep(ptr); + return 0; + } + ret = av_realloc(*ptrptr, size); + + if (!ret) { + av_freep(ptr); + return AVERROR(ENOMEM); + } + + *ptrptr = ret; + return 0; +} + +void *av_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (!size || nmemb >= INT_MAX / size) + return NULL; + return av_realloc(ptr, nmemb * size); +} + +int av_reallocp_array(void *ptr, size_t nmemb, size_t size) +{ + void **ptrptr = ptr; + void *ret; + if (!size || nmemb >= INT_MAX / size) + return AVERROR(ENOMEM); + if (!nmemb) { + av_freep(ptr); + return 0; + } + ret = av_realloc(*ptrptr, nmemb * size); + if (!ret) { + av_freep(ptr); + return AVERROR(ENOMEM); + } + *ptrptr = ret; + return 0; +} + void av_free(void *ptr) { - /* XXX: this test should not be needed on most libcs */ +#if CONFIG_MEMALIGN_HACK if (ptr) -#ifdef CONFIG_MEMALIGN_HACK - free((char*)ptr - ((char*)ptr)[-1]); + free((char *)ptr - ((char *)ptr)[-1]); +#elif HAVE_ALIGNED_MALLOC + _aligned_free(ptr); #else - free(ptr); + free(ptr); #endif } void av_freep(void *arg) { - void **ptr= (void**)arg; + void **ptr = (void **)arg; av_free(*ptr); *ptr = NULL; } -void *av_mallocz(unsigned int size) +void *av_mallocz(size_t size) { void *ptr = av_malloc(size); if (ptr) @@ -139,13 +212,188 @@ void *av_mallocz(unsigned int size) char *av_strdup(const char *s) { - char *ptr= NULL; - if(s){ - int len = strlen(s) + 1; - ptr = av_malloc(len); - if (ptr) - memcpy(ptr, s, len); + char *ptr = NULL; + if (s) { + int len = strlen(s) + 1; + ptr = av_realloc(NULL, len); + if (ptr) + memcpy(ptr, s, len); } return ptr; } +char *av_strndup(const char *s, size_t len) +{ + char *ret = NULL, *end; + + if (!s) + return NULL; + + end = memchr(s, 0, len); + if (end) + len = end - s; + + ret = av_realloc(NULL, len + 1); + if (!ret) + return NULL; + + memcpy(ret, s, len); + ret[len] = 0; + return ret; +} + +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++; + } +} + +void av_memcpy_backptr(uint8_t *dst, int back, int cnt) +{ + const uint8_t *src = &dst[-back]; + if (!back) + return; + + if (back == 1) { + memset(dst, *src, cnt); + } else if (back == 2) { + fill16(dst, cnt); + } else if (back == 3) { + fill24(dst, cnt); + } else if (back == 4) { + fill32(dst, cnt); + } else { + if (cnt >= 16) { + int blocklen = back; + while (cnt > blocklen) { + memcpy(dst, src, blocklen); + dst += blocklen; + cnt -= blocklen; + blocklen <<= 1; + } + memcpy(dst, src, cnt); + 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; + } + if (cnt) + *dst = *src; + } +} + +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; +} + +void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size) +{ + void **p = ptr; + if (min_size < *size) + return; + min_size = FFMAX(17 * min_size / 16 + 32, min_size); + av_free(*p); + *p = av_malloc(min_size); + if (!*p) + min_size = 0; + *size = min_size; +}