今天在翻看php源码时, 研究了一下str_repeat
的实现. 算法很有意思, 一般人的思路都是字符串拼接, 重复n次,就拼接n次, 时间复杂度为O(n). 但作者的思路很有意思, 把时间复杂度降到了log2^n+1.
上代码:
PHP_FUNCTION(str_repeat)
{
zend_string *input_str; /* Input string */
zend_long mult; /* Multiplier */
zend_string *result; /* Resulting string */
size_t result_len; /* Length of the resulting string */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl", &input_str, &mult) == FAILURE) {
return;
}
if (mult < 0) {
php_error_docref(NULL, E_WARNING, "Second argument has to be greater than or equal to 0");
return;
}
/* Don't waste our time if it's empty */
/* ... or if the multiplier is zero */
if (ZSTR_LEN(input_str) == 0 || mult == 0)
RETURN_EMPTY_STRING();
/* Initialize the result string */
result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
result_len = ZSTR_LEN(input_str) * mult;
/* Heavy optimization for situations where input string is 1 byte long */
if (ZSTR_LEN(input_str) == 1) {
memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
} else {
char *s, *e, *ee;
ptrdiff_t l=0;
memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
s = ZSTR_VAL(result); //猜测这里的变量名为start
e = ZSTR_VAL(result) + ZSTR_LEN(input_str); //这里应该是end
ee = ZSTR_VAL(result) + result_len; //这里应该是end ending, 最后的结尾
//重点看这里, 整个算法的核心
while (e<ee) {
//判断剩余内存的大小是否大于已经复制的内存大小, 取小者
l = (e-s) < (ee-e) ? (e-s) : (ee-e);
memmove(e, s, l);
e += l;
}
}
ZSTR_VAL(result)[result_len] = '\0';
RETURN_NEW_STR(result);
}
内存图:
第一次循环:
因为e-s < ee-e, 所以l取e-s, 也就是1
第二次循环:
因为e-s < ee-e, 所以l取e-s, 也就是2
第三次循环:
因为e-s > ee-e, 所以l取ee-e, 也就是2
第四次循环:
l=0