Skip to content

Commit

Permalink
Implement LRU cache
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsdos committed May 1, 2024
1 parent a66439a commit 04c6918
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 22 deletions.
155 changes: 133 additions & 22 deletions ext/bcmath/bcmath.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
ZEND_DECLARE_MODULE_GLOBALS(bcmath)
static PHP_GINIT_FUNCTION(bcmath);
static PHP_GSHUTDOWN_FUNCTION(bcmath);
static PHP_RINIT_FUNCTION(bcmath);
static PHP_RSHUTDOWN_FUNCTION(bcmath);
static PHP_MINIT_FUNCTION(bcmath);
static PHP_MSHUTDOWN_FUNCTION(bcmath);
static PHP_MINFO_FUNCTION(bcmath);
Expand All @@ -42,8 +44,8 @@ zend_module_entry bcmath_module_entry = {
ext_functions,
PHP_MINIT(bcmath),
PHP_MSHUTDOWN(bcmath),
NULL,
NULL,
PHP_RINIT(bcmath),
PHP_RSHUTDOWN(bcmath),
PHP_MINFO(bcmath),
PHP_BCMATH_VERSION,
PHP_MODULE_GLOBALS(bcmath),
Expand Down Expand Up @@ -82,6 +84,93 @@ PHP_INI_BEGIN()
PHP_INI_END()
/* }}} */

static void bcmath_destroy_lru_entry(php_bc_lru_entry *entry)
{
/* No need to clean up str as that reference is held by the table. */
bc_free_num(&entry->num);
}

static void bcmath_lru_init(php_bc_lru_cache *cache)
{
cache->head = cache->tail = NULL;
zend_hash_init(&cache->table, PHP_BCMATH_LRU_SIZE, NULL, NULL, false);
zend_hash_real_init_mixed(&cache->table);
}

static void bcmath_lru_destroy(php_bc_lru_cache *cache)
{
php_bc_lru_entry *entry;
ZEND_HASH_MAP_FOREACH_PTR(&cache->table, entry) {
bcmath_destroy_lru_entry(entry);
efree(entry);
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&cache->table);
}

static void bcmath_lru_new_entry(php_bc_lru_cache *cache, zend_string *str, bc_num num)
{
php_bc_lru_entry *entry = emalloc(sizeof(*entry));
entry->str = str;
entry->num = num;

entry->next = cache->head;
entry->prev = NULL;

if (cache->head) {
cache->head->prev = entry;
} else {
cache->tail = entry;
}

cache->head = entry;

zend_hash_add_new_ptr(&cache->table, str, entry);
}

static void bcmath_lru_move_to_head(php_bc_lru_cache *cache, php_bc_lru_entry *entry)
{
if (entry == cache->head) {
return;
}

if (entry == cache->tail) {
cache->tail = entry->prev;
}

if (entry->prev) {
entry->prev->next = entry->next;
}

if (entry->next) {
entry->next->prev = entry->prev;
}

entry->next = cache->head;
entry->prev = NULL;

cache->head->prev = entry;
cache->head = entry;
}

static void bcmath_lru_evict_oldest_and_reuse(php_bc_lru_cache *cache, zend_string *str, bc_num num)
{
zend_hash_del(&cache->table, cache->tail->str);
bcmath_destroy_lru_entry(cache->tail);
cache->tail->str = str;
cache->tail->num = num;
zend_hash_add_new_ptr(&cache->table, str, cache->tail);
bcmath_lru_move_to_head(cache, cache->tail);
}

static void bcmath_lru_add(php_bc_lru_cache *cache, zend_string *str, bc_num num)
{
if (zend_hash_num_elements(&cache->table) == PHP_BCMATH_LRU_SIZE) {
bcmath_lru_evict_oldest_and_reuse(cache, str, num);
} else {
bcmath_lru_new_entry(cache, str, num);
}
}

/* {{{ PHP_GINIT_FUNCTION */
static PHP_GINIT_FUNCTION(bcmath)
{
Expand All @@ -102,6 +191,18 @@ static PHP_GSHUTDOWN_FUNCTION(bcmath)
}
/* }}} */

static PHP_RINIT_FUNCTION(bcmath)
{
bcmath_lru_init(&bcmath_globals.lru_cache);
return SUCCESS;
}

static PHP_RSHUTDOWN_FUNCTION(bcmath)
{
bcmath_lru_destroy(&bcmath_globals.lru_cache);
return SUCCESS;
}

/* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(bcmath)
{
Expand Down Expand Up @@ -132,12 +233,22 @@ PHP_MINFO_FUNCTION(bcmath)

/* {{{ php_str2num
Convert to bc_num detecting scale */
static zend_result php_str2num(bc_num *num, char *str)
static zend_result php_str2num(bc_num *num, zend_string *str)
{
if (!bc_str2num(num, str, 0, true)) {
php_bc_lru_cache *cache = &BCG(lru_cache);
php_bc_lru_entry *cache_entry = zend_hash_find_ptr(&cache->table, str);
if (cache_entry) {
bcmath_lru_move_to_head(cache, cache_entry);
*num = bc_copy_num(cache_entry->num);
return SUCCESS;
}

if (!bc_str2num(num, ZSTR_VAL(str), 0, true)) {
return FAILURE;
}

bcmath_lru_add(cache, str, bc_copy_num(*num));

return SUCCESS;
}
/* }}} */
Expand Down Expand Up @@ -169,12 +280,12 @@ PHP_FUNCTION(bcadd)

bc_init_num(&result);

if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
if (php_str2num(&second, right) == FAILURE) {
zend_argument_value_error(2, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -218,12 +329,12 @@ PHP_FUNCTION(bcsub)

bc_init_num(&result);

if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
if (php_str2num(&second, right) == FAILURE) {
zend_argument_value_error(2, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -267,12 +378,12 @@ PHP_FUNCTION(bcmul)

bc_init_num(&result);

if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
if (php_str2num(&second, right) == FAILURE) {
zend_argument_value_error(2, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -316,12 +427,12 @@ PHP_FUNCTION(bcdiv)

bc_init_num(&result);

if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
if (php_str2num(&second, right) == FAILURE) {
zend_argument_value_error(2, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -368,12 +479,12 @@ PHP_FUNCTION(bcmod)

bc_init_num(&result);

if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
if (php_str2num(&second, right) == FAILURE) {
zend_argument_value_error(2, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -421,17 +532,17 @@ PHP_FUNCTION(bcpowmod)

bc_init_num(&result);

if (php_str2num(&bc_base, ZSTR_VAL(base_str)) == FAILURE) {
if (php_str2num(&bc_base, base_str) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

if (php_str2num(&bc_expo, ZSTR_VAL(exponent_str)) == FAILURE) {
if (php_str2num(&bc_expo, exponent_str) == FAILURE) {
zend_argument_value_error(2, "is not well-formed");
goto cleanup;
}

if (php_str2num(&bc_modulus, ZSTR_VAL(modulus_str)) == FAILURE) {
if (php_str2num(&bc_modulus, modulus_str) == FAILURE) {
zend_argument_value_error(3, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -495,12 +606,12 @@ PHP_FUNCTION(bcpow)

bc_init_num(&result);

if (php_str2num(&first, ZSTR_VAL(base_str)) == FAILURE) {
if (php_str2num(&first, base_str) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

if (php_str2num(&bc_exponent, ZSTR_VAL(exponent_str)) == FAILURE) {
if (php_str2num(&bc_exponent, exponent_str) == FAILURE) {
zend_argument_value_error(2, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -552,7 +663,7 @@ PHP_FUNCTION(bcsqrt)
scale = (int) scale_param;
}

if (php_str2num(&result, ZSTR_VAL(left)) == FAILURE) {
if (php_str2num(&result, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -625,7 +736,7 @@ static void bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAMETERS, bool is_floor)

bc_init_num(&result);

if (php_str2num(&num, ZSTR_VAL(numstr)) == FAILURE) {
if (php_str2num(&num, numstr) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}
Expand Down Expand Up @@ -686,7 +797,7 @@ PHP_FUNCTION(bcround)

bc_init_num(&result);

if (php_str2num(&num, ZSTR_VAL(numstr)) == FAILURE) {
if (php_str2num(&num, numstr) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}
Expand Down
14 changes: 14 additions & 0 deletions ext/bcmath/php_bcmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,31 @@
#include "zend_API.h"
#include "ext/standard/php_math_round_mode.h"

#define PHP_BCMATH_LRU_SIZE 16

extern zend_module_entry bcmath_module_entry;
#define phpext_bcmath_ptr &bcmath_module_entry

#include "php_version.h"
#define PHP_BCMATH_VERSION PHP_VERSION

typedef struct php_bc_lru_entry {
zend_string *str;
bc_num num;
struct php_bc_lru_entry *prev, *next;
} php_bc_lru_entry;

typedef struct php_bc_lru_cache {
HashTable table;
php_bc_lru_entry *head, *tail;
} php_bc_lru_cache;

ZEND_BEGIN_MODULE_GLOBALS(bcmath)
bc_num _zero_;
bc_num _one_;
bc_num _two_;
int bc_precision;
php_bc_lru_cache lru_cache;
ZEND_END_MODULE_GLOBALS(bcmath)

#if defined(ZTS) && defined(COMPILE_DL_BCMATH)
Expand Down

0 comments on commit 04c6918

Please sign in to comment.