Skip to content

Instantly share code, notes, and snippets.

@michael-grunder
Created September 27, 2022 17:26
Show Gist options
  • Save michael-grunder/1b1a5441b08f3990783e21bcc2051710 to your computer and use it in GitHub Desktop.
Save michael-grunder/1b1a5441b08f3990783e21bcc2051710 to your computer and use it in GitHub Desktop.
With a pointer timeout
static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, int kw_len, int min_argc, int has_timeout,
char **cmd, int *cmd_len, short *slot)
{
zval *z_args, *z_ele, *ztimeout = NULL;
HashTable *ht_arr;
char *key;
int key_free, i, tail;
size_t key_len;
int single_array = 0, argc = ZEND_NUM_ARGS();
smart_string cmdstr = {0};
short kslot = -1;
zend_string *zstr;
if (argc < min_argc) {
zend_wrong_param_count();
return FAILURE;
}
// Allocate args
z_args = emalloc(argc * sizeof(zval));
if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
efree(z_args);
return FAILURE;
}
// Handle our "single array" case
if (has_timeout == 0) {
single_array = argc==1 && Z_TYPE(z_args[0]) == IS_ARRAY;
} else {
single_array = argc==2 && Z_TYPE(z_args[0]) == IS_ARRAY &&
(Z_TYPE(z_args[1]) == IS_LONG || Z_TYPE(z_args[1]) == IS_DOUBLE);
if (single_array)
ztimeout = &z_args[1];
}
// If we're running a single array, rework args
if (single_array) {
ht_arr = Z_ARRVAL(z_args[0]);
argc = zend_hash_num_elements(ht_arr);
if (has_timeout) argc++;
efree(z_args);
z_args = NULL;
/* If the array is empty, we can simply abort */
if (argc == 0) return FAILURE;
}
// Begin construction of our command
redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len);
if (single_array) {
ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) {
zstr = zval_get_string(z_ele);
key = ZSTR_VAL(zstr);
key_len = ZSTR_LEN(zstr);
key_free = redis_key_prefix(redis_sock, &key, &key_len);
// Protect against CROSSLOT errors
if (slot) {
if (kslot == -1) {
kslot = cluster_hash_key(key, key_len);
} else if (cluster_hash_key(key,key_len)!=kslot) {
zend_string_release(zstr);
if (key_free) efree(key);
php_error_docref(NULL, E_WARNING,
"Not all keys hash to the same slot!");
return FAILURE;
}
}
// Append this key, free it if we prefixed
redis_cmd_append_sstr(&cmdstr, key, key_len);
zend_string_release(zstr);
if (key_free) efree(key);
} ZEND_HASH_FOREACH_END();
if (ztimeout) {
ZEND_ASSERT(Z_TYPE_P(ztimeout) == IS_LONG || Z_TYPE_P(ztimeout) == IS_DOUBLE);
if (Z_TYPE_P(ztimeout) == IS_LONG) {
redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(ztimeout));
} else {
redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(ztimeout));
}
}
} else {
if (has_timeout) {
zend_uchar type = Z_TYPE(z_args[argc - 1]);
if (type == IS_LONG || type == IS_DOUBLE) {
ztimeout = &z_args[argc - 1];
} else {
php_error_docref(NULL, E_ERROR, "Timeout value must be a long or double");
efree(z_args);
return FAILURE;
}
}
tail = has_timeout ? argc-1 : argc;
for(i = 0; i < tail; i++) {
zstr = zval_get_string(&z_args[i]);
key = ZSTR_VAL(zstr);
key_len = ZSTR_LEN(zstr);
key_free = redis_key_prefix(redis_sock, &key, &key_len);
/* Protect against CROSSSLOT errors if we've got a slot */
if (slot) {
if ( kslot == -1) {
kslot = cluster_hash_key(key, key_len);
} else if (cluster_hash_key(key,key_len)!=kslot) {
php_error_docref(NULL, E_WARNING,
"Not all keys hash to the same slot");
zend_string_release(zstr);
if (key_free) efree(key);
efree(z_args);
return FAILURE;
}
}
// Append this key
redis_cmd_append_sstr(&cmdstr, key, key_len);
zend_string_release(zstr);
if (key_free) efree(key);
}
if (ztimeout) {
ZEND_ASSERT(Z_TYPE_P(ztimeout) == IS_LONG || Z_TYPE_P(ztimeout) == IS_DOUBLE);
if (Z_TYPE_P(ztimeout) == IS_DOUBLE) {
redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(ztimeout));
} else {
redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(ztimeout));
}
}
// Cleanup args
efree(z_args);
}
// Push out parameters
if (slot) *slot = kslot;
*cmd = cmdstr.c;
*cmd_len = cmdstr.len;
return SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment