在Libev中,如果某种结构的数组需要扩容,它使用array_needsize宏进行处理,比如:
array_needsize (int, fdchanges, fdchangemax, fdchangecnt, EMPTY2);
这就表示要将整型(int)数组fdchanges,由原来的fdchangemax个元素扩容为fdchangecnt,新扩容的内存空间使用EMPTR2进行初始化。
array_needsize宏定义如下:
#define array_needsize(type,base,cur,cnt,init) if (expect_false ((cnt) > (cur))) { int ecb_unused ocur_ = (cur); (base) = (type *)array_realloc(sizeof(type), (base), &(cur), (cnt)); init ((base) + (ocur_), (cur) - ocur_); }
base是type类型的数组,当前有cur个元素,需要调整到cnt个元素,新扩充的内存空间使用init函数初始化。
该宏的关键在于array_realloc函数,它的实现如下:
static void * array_realloc (int elem, void *base, int *cur, int cnt) { *cur = array_nextsize (elem, *cur, cnt); return ev_realloc (base, elem * *cur); }
该函数中,首先使用array_nextsize计算最终的元素个数,然后调用ev_realloc申请空间。array_nextsize计算新元素个数的方法如下:
/* find a suitable new size for the given array, */ /* hopefully by rounding to a nice-to-malloc size */ int array_nextsize (int elem, int cur, int cnt) { int ncur = cur + 1; do ncur <<= 1; while (cnt > ncur); /* if size is large, round to MALLOC_ROUND - 4 * longs to accommodate malloc overhead */ if (elem * ncur > MALLOC_ROUND - sizeof (void *) * 4) { ncur *= elem; ncur = (ncur + elem + (MALLOC_ROUND - 1) + sizeof (void *) * 4) & ~(MALLOC_ROUND - 1); ncur = ncur - sizeof (void *) * 4; ncur /= elem; } return ncur; }
该函数中,首先得到一个比cnt大的偶数ncur,如果ncur个元素占用的空间(elem* ncur + sizeof (void *) * 4)大于MALLOC_ROUND(4096)个字节,则需要调整ncur。
这里之所以要加上sizeof(void *) * 4,是因为malloc在申请空间时,除了申请的字节数之外,它还会在内存块之外加上额外的空间,记录当前内存块的信息,也就是sizeof (void *) * 4个字节。
调整ncur的方法,主要是下面的语句:
ncur *= elem; ncur = (ncur + elem + (MALLOC_ROUND - 1) + sizeof (void *) * 4) & ~(MALLOC_ROUND - 1);
它的主要作用,就是使得ncur向上调整成MALLOC_ROUND的倍数。这里的ncur代表的是最终申请空间的总字节数。因此,还需要将其调整为元素个数:
ncur = ncur - sizeof (void *) * 4; ncur /= elem;
得到最终的元素个数之后,接下来就是调用ev_realloc申请空间了,它的实现如下:
static void *ev_realloc_emul (void *ptr, long size) EV_THROW { /* some systems, notably openbsd and darwin, fail to properly * implement realloc (x, 0) (as required by both ansi c-89 and * the single unix specification, so work around them here. * recently, also (at least) fedora and debian started breaking it, * despite documenting it otherwise. */ if (size) return realloc (ptr, size); free (ptr); return 0; } static void *(*alloc)(void *ptr, long size) = ev_realloc_emul; void *ev_realloc (void *ptr, long size) { ptr = alloc (ptr, size); if (!ptr && size) { fprintf (stderr, "(libev) cannot allocate %ld bytes, aborting.", size); abort (); } return ptr; }
PS:Libev的代码就分析到这了!