PHP 内核源码 Array 初入一

array.c 文件

/* {{{ proto bool sort(array &array_arg [, int sort_flags])
   Sort an array */
PHP_FUNCTION(sort)
{
    zval *array;
    zend_long sort_type = PHP_SORT_REGULAR;
    compare_func_t cmp;

    ZEND_PARSE_PARAMETERS_START(1, 2)
        Z_PARAM_ARRAY_EX(array, 0, 1)
        Z_PARAM_OPTIONAL
        Z_PARAM_LONG(sort_type)
    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

    cmp = php_get_data_compare_func(sort_type, 0);

    if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) {
        RETURN_FALSE;
    }
    RETURN_TRUE;
}
/* }}} */

根据上述代码,查找 zend_hash_sort 函数,找到 zend_hash.h && zend_hash.c

zend_hash.h,zend_hash_sort 函数在 zend_hash.c 实现

/* Copying, merging and sorting */
ZEND_API void  ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor);
ZEND_API void  ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, zend_bool overwrite);
ZEND_API void  ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam);
ZEND_API void  zend_hash_bucket_swap(Bucket *p, Bucket *q);
ZEND_API void  zend_hash_bucket_renum_swap(Bucket *p, Bucket *q);
ZEND_API void  zend_hash_bucket_packed_swap(Bucket *p, Bucket *q);
ZEND_API int   zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered);
ZEND_API int   ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, compare_func_t compare_func, zend_bool renumber);
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag);

#define zend_hash_sort(ht, compare_func, renumber) \
    zend_hash_sort_ex(ht, zend_sort, compare_func, renumber)

zend_hash.c

ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, compare_func_t compar, zend_bool renumber)
{
    Bucket *p;
    uint32_t i, j;

    IS_CONSISTENT(ht);
    HT_ASSERT_RC1(ht);

    if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
        return SUCCESS;
    }

    if (HT_IS_WITHOUT_HOLES(ht)) {
        i = ht->nNumUsed;
    } else {
        for (j = 0, i = 0; j < ht->nNumUsed; j++) {
            p = ht->arData + j;
            if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
            if (i != j) {
                ht->arData[i] = *p;
            }
            i++;
        }
    }

    sort((void *)ht->arData, i, sizeof(Bucket), compar,
            (swap_func_t)(renumber? zend_hash_bucket_renum_swap :
                ((HT_FLAGS(ht) & HASH_FLAG_PACKED) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));

    ht->nNumUsed = i;
    ht->nInternalPointer = 0;

    if (renumber) {
        for (j = 0; j < i; j++) {
            p = ht->arData + j;
            p->h = j;
            if (p->key) {
                zend_string_release(p->key);
                p->key = NULL;
            }
        }

        ht->nNextFreeElement = i;
    }
    if (HT_FLAGS(ht) & HASH_FLAG_PACKED) {
        if (!renumber) {
            zend_hash_packed_to_hash(ht);
        }
    } else {
        if (renumber) {
            void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
            Bucket *old_buckets = ht->arData;

            new_data = pemalloc(HT_SIZE_EX(ht->nTableSize, HT_MIN_MASK), (GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
            HT_FLAGS(ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
            ht->nTableMask = HT_MIN_MASK;
            HT_SET_DATA_ADDR(ht, new_data);
            memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
            pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
            HT_HASH_RESET_PACKED(ht);
        } else {
            zend_hash_rehash(ht);
        }
    }

    return SUCCESS;
}

由上述代码,我们来看 zend_sort 函数实现。
打开 zend_sort.h && zend_sort.c
zend_sort.h

#ifndef ZEND_SORT_H
#define ZEND_SORT_H

BEGIN_EXTERN_C()
ZEND_API void zend_qsort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp);
ZEND_API void zend_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp);
ZEND_API void zend_insert_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp);
END_EXTERN_C()

#endif       /* ZEND_SORT_H */

zend_sort.c 实现了以上三个函数

qsort

ZEND_API void zend_qsort(void *base, size_t nmemb, size_t siz, compare_func_t compare, swap_func_t swp) /* {{{ */
{
    void           *begin_stack[QSORT_STACK_SIZE];
    void           *end_stack[QSORT_STACK_SIZE];
    register char  *begin;
    register char  *end;
    register char  *seg1;
    register char  *seg2;
    register char  *seg2p;
    register int    loop;
    size_t          offset;

    begin_stack[0] = (char *) base;
    end_stack[0]   = (char *) base + ((nmemb - 1) * siz);

    for (loop = 0; loop >= 0; --loop) {
        begin = begin_stack[loop];
        end   = end_stack[loop];

        while (begin < end) {
            offset = (end - begin) >> Z_L(1);
            swp(begin, begin + (offset - (offset % siz)));

            seg1 = begin + siz;
            seg2 = end;

            while (1) {
                for (; seg1 < seg2 && compare(begin, seg1) > 0;
                     seg1 += siz);

                for (; seg2 >= seg1 && compare(seg2, begin) > 0;
                     seg2 -= siz);

                if (seg1 >= seg2)
                    break;

                swp(seg1, seg2);

                seg1 += siz;
                seg2 -= siz;
            }

            swp(begin, seg2);

            seg2p = seg2;

            if ((seg2p - begin) <= (end - seg2p)) {
                if ((seg2p + siz) < end) {
                    begin_stack[loop] = seg2p + siz;
                    end_stack[loop++] = end;
                }
                end = seg2p - siz;
            }
            else {
                if ((seg2p - siz) > begin) {
                    begin_stack[loop] = begin;
                    end_stack[loop++] = seg2p - siz;
                }
                begin = seg2p + siz;
            }
        }
    }
}

sort

ZEND_API void zend_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp)
{
    while (1) {
        if (nmemb <= 16) {
            zend_insert_sort(base, nmemb, siz, cmp, swp);
            return;
        } else {
            char *i, *j;
            char *start = (char *)base;
            char *end = start + (nmemb * siz);
            size_t offset = (nmemb >> Z_L(1));
            char *pivot = start + (offset * siz);

            if ((nmemb >> Z_L(10))) {
                size_t delta = (offset >> Z_L(1)) * siz;
                zend_sort_5(start, start + delta, pivot, pivot + delta, end - siz, cmp, swp);
            } else {
                zend_sort_3(start, pivot, end - siz, cmp, swp);
            }
            swp(start + siz, pivot);
            pivot = start + siz;
            i = pivot + siz;
            j = end - siz;
            while (1) {
                while (cmp(pivot, i) > 0) {
                    i += siz;
                    if (UNEXPECTED(i == j)) {
                        goto done;
                    }
                }
                j -= siz;
                if (UNEXPECTED(j == i)) {
                    goto done;
                }
                while (cmp(j, pivot) > 0) {
                    j -= siz;
                    if (UNEXPECTED(j == i)) {
                        goto done;
                    }
                }
                swp(i, j);
                i += siz;
                if (UNEXPECTED(i == j)) {
                    goto done;
                }
            }
done:
            swp(pivot, i - siz);
            if ((i - siz) - start < end - i) {
                zend_sort(start, (i - start)/siz - 1, siz, cmp, swp);
                base = i;
                nmemb = (end - i)/siz;
            } else {
                zend_sort(i, (end - i)/siz, siz, cmp, swp);
                nmemb = (i - start)/siz - 1;
            }
        }
    }
}

insert_sort

ZEND_API void zend_insert_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp) /* {{{ */{
    switch (nmemb) {
        case 0:
        case 1:
            break;
        case 2:
            zend_sort_2(base, (char *)base + siz, cmp, swp);
            break;
        case 3:
            zend_sort_3(base, (char *)base + siz, (char *)base + siz + siz, cmp, swp);
            break;
        case 4:
            {
                size_t siz2 = siz + siz;
                zend_sort_4(base, (char *)base + siz, (char *)base + siz2, (char *)base + siz + siz2, cmp, swp);
            }
            break;
        case 5:
            {
                size_t siz2 = siz + siz;
                zend_sort_5(base, (char *)base + siz, (char *)base + siz2, (char *)base + siz + siz2, (char *)base + siz2 + siz2, cmp, swp);
            }
            break;
        default:
            {
                char *i, *j, *k;
                char *start = (char *)base;
                char *end = start + (nmemb * siz);
                size_t siz2= siz + siz;
                char *sentry = start + (6 * siz);
                for (i = start + siz; i < sentry; i += siz) {
                    j = i - siz;
                    if (!(cmp(j, i) > 0)) {
                        continue;
                    }
                    while (j != start) {
                        j -= siz;
                        if (!(cmp(j, i) > 0)) {
                            j += siz;
                            break;
                        }
                    }
                    for (k = i; k > j; k -= siz) {
                        swp(k, k - siz);
                    }
                }
                for (i = sentry; i < end; i += siz) {
                    j = i - siz;
                    if (!(cmp(j, i) > 0)) {
                        continue;
                    }
                    do {
                        j -= siz2;
                        if (!(cmp(j, i) > 0)) {
                            j += siz;
                            if (!(cmp(j, i) > 0)) {
                                j += siz;
                            }
                            break;
                        }
                        if (j == start) {
                            break;
                        }
                        if (j == start + siz) {
                            j -= siz;
                            if (cmp(i, j) > 0) {
                                j += siz;
                            }
                            break;
                        }
                    } while (1);
                    for (k = i; k > j; k -= siz) {
                        swp(k, k - siz);
                    }
                }
            }
            break;
    }
}

你可能感兴趣的:(PHP 内核源码 Array 初入一)