Commit 37b0f361 authored by Oran Agra's avatar Oran Agra
Browse files

Adjustments to recent RM_StringTruncate fix (#3718) (#9125)

- Introduce a new sdssubstr api as a building block for sdsrange.
  The API of sdsrange is many times hard to work with and also has
  corner case that cause bugs. sdsrange is easy to work with and also
  simplifies the implementation of sdsrange.
- Revert the fix to RM_StringTruncate and just use sdssubstr instead of
  sdsrange.
- Solve valgrind warnings from the new tests introduced by the previous
  PR.

(cherry picked from commit ae418eca)
parent 68661171
...@@ -2538,19 +2538,14 @@ int RM_StringTruncate(RedisModuleKey *key, size_t newlen) { ...@@ -2538,19 +2538,14 @@ int RM_StringTruncate(RedisModuleKey *key, size_t newlen) {
} else { } else {
/* Unshare and resize. */ /* Unshare and resize. */
key->value = dbUnshareStringValue(key->db, key->key, key->value); key->value = dbUnshareStringValue(key->db, key->key, key->value);
if (newlen == 0) { size_t curlen = sdslen(key->value->ptr);
sdsfree(key->value->ptr); if (newlen > curlen) {
key->value->ptr = sdsempty(); key->value->ptr = sdsgrowzero(key->value->ptr,newlen);
} else { } else if (newlen < curlen) {
size_t curlen = sdslen(key->value->ptr); sdssubstr(key->value->ptr,0,newlen);
if (newlen > curlen) { /* If the string is too wasteful, reallocate it. */
key->value->ptr = sdsgrowzero(key->value->ptr,newlen); if (sdslen(key->value->ptr) < sdsavail(key->value->ptr))
} else if (newlen < curlen) { key->value->ptr = sdsRemoveFreeSpace(key->value->ptr);
sdsrange(key->value->ptr,0,newlen-1);
/* If the string is too wasteful, reallocate it. */
if (sdslen(key->value->ptr) < sdsavail(key->value->ptr))
key->value->ptr = sdsRemoveFreeSpace(key->value->ptr);
}
} }
} }
return REDISMODULE_OK; return REDISMODULE_OK;
......
...@@ -759,6 +759,21 @@ sds sdstrim(sds s, const char *cset) { ...@@ -759,6 +759,21 @@ sds sdstrim(sds s, const char *cset) {
return s; return s;
} }
/* Changes the input string to be a subset of the original.
* It does not release the free space in the string, so a call to
* sdsRemoveFreeSpace may be wise after. */
void sdssubstr(sds s, size_t start, size_t len) {
/* Clamp out of range input */
size_t oldlen = sdslen(s);
if (start >= oldlen) start = len = 0;
if (len > oldlen-start) len = oldlen-start;
/* Move the data */
if (len) memmove(s, s+start, len);
s[len] = 0;
sdssetlen(s,len);
}
/* Turn the string into a smaller (or equal) string containing only the /* Turn the string into a smaller (or equal) string containing only the
* substring specified by the 'start' and 'end' indexes. * substring specified by the 'start' and 'end' indexes.
* *
...@@ -770,6 +785,11 @@ sds sdstrim(sds s, const char *cset) { ...@@ -770,6 +785,11 @@ sds sdstrim(sds s, const char *cset) {
* *
* The string is modified in-place. * The string is modified in-place.
* *
* NOTE: this function can be misleading and can have unexpected behaviour,
* specifically when you want the length of the new string to be 0.
* Having start==end will result in a string with one character.
* please consider using sdssubstr instead.
*
* Example: * Example:
* *
* s = sdsnew("Hello World"); * s = sdsnew("Hello World");
...@@ -777,28 +797,13 @@ sds sdstrim(sds s, const char *cset) { ...@@ -777,28 +797,13 @@ sds sdstrim(sds s, const char *cset) {
*/ */
void sdsrange(sds s, ssize_t start, ssize_t end) { void sdsrange(sds s, ssize_t start, ssize_t end) {
size_t newlen, len = sdslen(s); size_t newlen, len = sdslen(s);
if (len == 0) return; if (len == 0) return;
if (start < 0) { if (start < 0)
start = len+start; start = len + start;
if (start < 0) start = 0; if (end < 0)
} end = len + end;
if (end < 0) {
end = len+end;
if (end < 0) end = 0;
}
newlen = (start > end) ? 0 : (end-start)+1; newlen = (start > end) ? 0 : (end-start)+1;
if (newlen != 0) { sdssubstr(s, start, newlen);
if (start >= (ssize_t)len) {
newlen = 0;
} else if (end >= (ssize_t)len) {
end = len-1;
newlen = (start > end) ? 0 : (end-start)+1;
}
}
if (start && newlen) memmove(s, s+start, newlen);
s[newlen] = 0;
sdssetlen(s,newlen);
} }
/* Apply tolower() to every character of the sds string 's'. */ /* Apply tolower() to every character of the sds string 's'. */
...@@ -1353,6 +1358,18 @@ int sdsTest(int argc, char **argv, int accurate) { ...@@ -1353,6 +1358,18 @@ int sdsTest(int argc, char **argv, int accurate) {
test_cond("sdsrange(...,100,100)", test_cond("sdsrange(...,100,100)",
sdslen(y) == 0 && memcmp(y,"\0",1) == 0); sdslen(y) == 0 && memcmp(y,"\0",1) == 0);
sdsfree(y);
y = sdsdup(x);
sdsrange(y,4,6);
test_cond("sdsrange(...,4,6)",
sdslen(y) == 0 && memcmp(y,"\0",1) == 0);
sdsfree(y);
y = sdsdup(x);
sdsrange(y,3,6);
test_cond("sdsrange(...,3,6)",
sdslen(y) == 1 && memcmp(y,"o\0",2) == 0);
sdsfree(y); sdsfree(y);
sdsfree(x); sdsfree(x);
x = sdsnew("foo"); x = sdsnew("foo");
......
...@@ -238,6 +238,7 @@ sds sdscatprintf(sds s, const char *fmt, ...); ...@@ -238,6 +238,7 @@ sds sdscatprintf(sds s, const char *fmt, ...);
sds sdscatfmt(sds s, char const *fmt, ...); sds sdscatfmt(sds s, char const *fmt, ...);
sds sdstrim(sds s, const char *cset); sds sdstrim(sds s, const char *cset);
void sdssubstr(sds s, size_t start, size_t len);
void sdsrange(sds s, ssize_t start, ssize_t end); void sdsrange(sds s, ssize_t start, ssize_t end);
void sdsupdatelen(sds s); void sdsupdatelen(sds s);
void sdsclear(sds s); void sdsclear(sds s);
......
...@@ -156,6 +156,7 @@ int TestUnlink(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { ...@@ -156,6 +156,7 @@ int TestUnlink(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
/* TEST.STRING.TRUNCATE -- Test truncating an existing string object. */ /* TEST.STRING.TRUNCATE -- Test truncating an existing string object. */
int TestStringTruncate(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int TestStringTruncate(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_AutoMemory(ctx);
REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc); REDISMODULE_NOT_USED(argc);
...@@ -208,6 +209,7 @@ int TestStringTruncate(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -208,6 +209,7 @@ int TestStringTruncate(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
int NotifyCallback(RedisModuleCtx *ctx, int type, const char *event, int NotifyCallback(RedisModuleCtx *ctx, int type, const char *event,
RedisModuleString *key) { RedisModuleString *key) {
RedisModule_AutoMemory(ctx);
/* Increment a counter on the notifications: for each key notified we /* Increment a counter on the notifications: for each key notified we
* increment a counter */ * increment a counter */
RedisModule_Log(ctx, "notice", "Got event type %d, event %s, key %s", type, RedisModule_Log(ctx, "notice", "Got event type %d, event %s, key %s", type,
...@@ -219,6 +221,7 @@ int NotifyCallback(RedisModuleCtx *ctx, int type, const char *event, ...@@ -219,6 +221,7 @@ int NotifyCallback(RedisModuleCtx *ctx, int type, const char *event,
/* TEST.NOTIFICATIONS -- Test Keyspace Notifications. */ /* TEST.NOTIFICATIONS -- Test Keyspace Notifications. */
int TestNotifications(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int TestNotifications(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_AutoMemory(ctx);
REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc); REDISMODULE_NOT_USED(argc);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment