Skip to content

Commit

Permalink
add some redis list commands (twitter#201)
Browse files Browse the repository at this point in the history
* add some redis list commands

* Implement list push, address comments and refactor
  - Implement list push command
  - Add ziplist_truncate and unit tests
  - Refactor ziplist_trim to use ziplist_truncate
  - Local var optimization for ziplist_remove_val count
  - Inline item_will_fit, expose slab profile
  - Move redis-isms out of data structure and into ds process
  - Minor deduplication/refactoring of list command module
  • Loading branch information
kevyang authored and michalbiesek committed Jul 17, 2019
1 parent bd15194 commit 091cf13
Show file tree
Hide file tree
Showing 11 changed files with 939 additions and 64 deletions.
92 changes: 85 additions & 7 deletions src/data_structure/ziplist/ziplist.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ _ziplist_prev(zipentry_p ze)
return ze - *(ze - 1); /* *(ze - 1) : length of the previous entry */
}

/* do NOT call this function on the last zip entry, use ziplist_prev */
/* do NOT call this function on the last zip entry, use ziplist_next */
static inline zipentry_p
_ziplist_next(zipentry_p ze)
{
Expand Down Expand Up @@ -343,6 +343,7 @@ ziplist_remove_val(uint32_t *removed, ziplist_p zl, const struct blob *val,
zipentry_p z;
uint8_t *end;
bool forward = (count > 0);
uint32_t nrem = 0;

if (zl == NULL || val == NULL) {
return ZIPLIST_ERROR;
Expand All @@ -354,11 +355,10 @@ ziplist_remove_val(uint32_t *removed, ziplist_p zl, const struct blob *val,
}

if (count == 0) {
return ZIPLIST_EINVALID;
goto done;
}

atmost = forward ? count : -count;
*removed = 0;

/* Encoding one struct blob and follow up with many simple memcmp should be
* faster than decoding each of the zentries being compared.
Expand All @@ -372,19 +372,24 @@ ziplist_remove_val(uint32_t *removed, ziplist_p zl, const struct blob *val,
while (memcmp(z, ze_buf, MIN(end - z + 1, len)) != 0) {
if (forward) {
if (z == _ziplist_tail(zl)) {
return ZIPLIST_OK;
goto done;
}
z = _ziplist_next(z);
} else {
if (z == _ziplist_head(zl)) {
return ZIPLIST_OK;
goto done;
}
z = _ziplist_prev(z);
}
}

_ziplist_remove(zl, z, _ziplist_next(z), 1);
*removed += 1;
++nrem;
}

done:
if (removed != NULL) {
*removed = nrem;
}

return ZIPLIST_OK;
Expand All @@ -402,7 +407,7 @@ ziplist_remove(ziplist_p zl, int64_t idx, int64_t count)
}

if (count == 0) {
return ZIPLIST_EINVALID;
return ZIPLIST_OK;
}

nentry = ziplist_nentry(zl);
Expand Down Expand Up @@ -470,6 +475,79 @@ ziplist_insert(ziplist_p zl, struct blob *val, int64_t idx)
return ZIPLIST_OK;
}

ziplist_rstatus_e
ziplist_trim(ziplist_p zl, int64_t idx, int64_t count)
{
ziplist_rstatus_e status;

if (zl == NULL) {
return ZIPLIST_ERROR;
}

idx += (idx < 0) * ziplist_nentry(zl);
if (idx < 0 || idx >= ziplist_nentry(zl)) {
return ZIPLIST_EOOB;
}

if (count > 0) { /* counting forward from idx */
/* remove from begin to idx */
status = ziplist_truncate(zl, idx);
ASSERT(status == ZIPLIST_OK);

/* truncate to count entries from end */
if (count < ziplist_nentry(zl)) {
status = ziplist_truncate(zl, -(ziplist_nentry(zl) - count));
ASSERT(status == ZIPLIST_OK);
}
} else { /* counting backward from idx */
/* remove from end to idx */
status = ziplist_truncate(zl, -(ziplist_nentry(zl) - idx));
ASSERT(status == ZIPLIST_OK);

/* truncate to -count entries from beginning */
if (-count < ziplist_nentry(zl)) {
status = ziplist_truncate(zl, ziplist_nentry(zl) + count);
ASSERT(status == ZIPLIST_OK);
}
}

return ZIPLIST_OK;
}

ziplist_rstatus_e
ziplist_truncate(ziplist_p zl, int64_t count)
{
zipentry_p begin, end;
ziplist_rstatus_e status;

if (zl == NULL) {
return ZIPLIST_ERROR;
}

if (count == 0) {
return ZIPLIST_OK;
}

/* if abs(count) >= num entries in ziplist, remove all */
if (count >= ziplist_nentry(zl) || -count >= ziplist_nentry(zl)) {
return ziplist_reset(zl);
}

if (count > 0) {
begin = _ziplist_head(zl);
status = ziplist_locate(&end, zl, count);
ASSERT(status == ZIPLIST_OK);
} else {
count = -count;
end = (zipentry_p)_ziplist_end(zl) + 1;
status = ziplist_locate(&begin, zl, ziplist_nentry(zl) - count);
ASSERT(status == ZIPLIST_OK);
}

_ziplist_remove(zl, begin, end, count);
return ZIPLIST_OK;
}

ziplist_rstatus_e
ziplist_push(ziplist_p zl, struct blob *val)
{
Expand Down
16 changes: 16 additions & 0 deletions src/data_structure/ziplist/ziplist.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,22 @@ ziplist_rstatus_e ziplist_remove_val(uint32_t *removed, ziplist_p zl, const stru
* CALLER MUST MAKE SURE THERE IS ENOUGH MEMORY!!!
*/
ziplist_rstatus_e ziplist_insert(ziplist_p zl, struct blob *val, int64_t idx);
/*
* trim list down to contain at most count entries, starting at idx. a negative count means
* starting from the end, and gives a list that is non inclusive of idx.
*
* e.g.
* if list contains { 0, 1, 2, 3, 4, 5 }, and we call ziplist_trim(zl, 4, -3),
* the trimmed list will be { 1, 2, 3 }.
*
* if there are fewer than count entries, all entries starting at idx are preserved
*/
ziplist_rstatus_e ziplist_trim(ziplist_p zl, int64_t idx, int64_t count);
/*
* if count is positive, remove count entries starting at the beginning
* if count is negative, remove -count entries starting at the end
*/
ziplist_rstatus_e ziplist_truncate(ziplist_p zl, int64_t count);
ziplist_rstatus_e ziplist_push(ziplist_p zl, struct blob *val); /* a shorthand for insert at idx == nentry */
/* remove tail & return, if val is NULL it is equivalent to remove at idx -1 */
ziplist_rstatus_e ziplist_pop(struct blob *val, ziplist_p zl);
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/data/redis/cmd_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* List.delete KEY [VALUE [COUNT]]
*
* trim: trimming a list
* List.trim KEY INDEX [COUNT]
* List.trim KEY INDEX COUNT
*
* len: return number of entries in list
* List.len KEY
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/data/redis/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ typedef enum cmd_type {
#undef GET_TYPE

/*
* Note: though redis supports unboudned number of variables in some commands,
* Note: though redis supports unbounded number of variables in some commands,
* implementation cannot operate with performance guarantee when this number
* gets too big. It also introduces uncertainty around resources. Therefore, we
* are limiting it to REQ_NTOKEN minus the # required args. For each command, if
Expand Down
Loading

0 comments on commit 091cf13

Please sign in to comment.