Trimming strings and getting ranges
String trimming is a common operation where a set of characters are removed from the left and the right of the string. Another useful operation regarding strings is the ability to just take a range out of a larger string.
void sdstrim(sds s, const char *cset);
void sdsrange(sds s, int start, int end);
SDS provides both the operations with the sdstrim and sdsrange functions.
However note that both functions work differently than most functions modifying
SDS strings since the return value is null: basically those functions always
destructively modify the passed SDS string, never allocating a new one, because
both trimming and ranges will never need more room: the operations can only
remove characters from the original strings.
Because of this behavior, both functions are fast and don't involve reallocation.
This is an example of string trimming where newlines and spaces are removed from an SDS strings:
sds s = sdsnew(" my string\n\n ");
sdstrim(s," \n");
printf("-%s-\n",s);
output> -my string-
Basically sdstrim takes the SDS string to trim as first argument, and a
null terminated set of characters to remove from left and right of the string.
The characters are removed as long as they are not interrupted by a character
that is not in the list of characters to trim: this is why the space between
"my" and "string" was preserved in the above example.
Taking ranges is similar, but instead to take a set of characters, it takes to indexes, representing the start and the end as specified by zero-based indexes inside the string, to obtain the range that will be retained.
sds s = sdsnew("Hello World!");
sdsrange(s,1,4);
printf("-%s-\n");
output> -ello-
Indexes can be negative to specify a position starting from the end of the
string, so that -1 means the last character, -2 the penultimate, and so forth:
sds s = sdsnew("Hello World!");
sdsrange(s,6,-1);
printf("-%s-\n");
sdsrange(s,0,-2);
printf("-%s-\n");
output> -World!-
output> -World-
sdsrange is very useful when implementing networking servers processing
a protocol or sending messages. For example the following code is used
implementing the write handler of the Redis Cluster message bus between
nodes:
void clusterWriteHandler(..., int fd, void *privdata, ...) {
clusterLink *link = (clusterLink*) privdata;
ssize_t nwritten = write(fd, link->sndbuf, sdslen(link->sndbuf));
if (nwritten <= 0) {
/* Error handling... */
}
sdsrange(link->sndbuf,nwritten,-1);
... more code here ...
}
Every time the socket of the node we want to send the message to is writable
we attempt to write as much bytes as possible, and we use sdsrange in order
to remove from the buffer what was already sent.
The function to queue new messages to send to some node in the cluster will
simply use sdscatlen in order to put more data in the send buffer.
Note that the Redis Cluster bus implements a binary protocol, but since SDS is binary safe this is not a problem, so the goal of SDS is not just to provide an high level string API for the C programmer but also dynamically allocated buffers that are easy to manage.
String copying
The most dangerous and infamus function of the standard C library is probably
strcpy, so perhaps it is funny how in the context of better designed dynamic
string libraries the concept of copying strings is almost irrelevant. Usually
what you do is to create strings with the content you want, or concatenating
more content as needed.
However SDS features a string copy function that is useful in performance critical code sections, however I guess its practical usefulness is limited as the function never managed to get called in the context of the 50k lines of code composing the Redis code base.
sds sdscpylen(sds s, const char *t, size_t len);
sds sdscpy(sds s, const char *t);
The string copy function of SDS is called sdscpylen and works like that:
s = sdsnew("Hello World!");
s = sdscpylen(s,"Hello Superman!",15);
As you can see the function receives as input the SDS string s, but also
returns an SDS string. This is common to many SDS functions that modify the
string: this way the returned SDS string may be the original one modified
or a newly allocated one (for example if there was not enough room in the
old SDS string).
The sdscpylen will simply replace what was in the old SDS string with the
new data you pass using the pointer and length argument. There is a similar
function called sdscpy that does not need a length but expects a null
terminated string instead.
You may wonder why it makes sense to have a string copy function in the
SDS library, since you can simply create a new SDS string from scratch
with the new value instead of copying the value in an existing SDS string.
The reason is efficiency: sdsnewlen will always allocate a new string
while sdscpylen will try to reuse the existing string if there is enough
room to old the new content specified by the user, and will allocate a new
one only if needed.
Quoting strings
In order to provide consistent output to the program user, or for debugging purposes, it is often important to turn a string that may contain binary data or special characters into a quoted string. Here for quoted string we mean the common format for String literals in programming source code. However today this format is also part of the well known serialization formats like JSON and CSV, so it definitely escaped the simple gaol of representing literals strings in the source code of programs.
An example of quoted string literal is the following:
"\x00Hello World\n"
The first byte is a zero byte while the last byte is a newline, so there are two non alphanumerical characters inside the string.
SDS uses a concatenation function for this goal, that concatenates to an existing string the quoted string representation of the input string.
sds sdscatrepr(sds s, const char *p, size_t len);
The scscatrepr (where repr means representation) follows the usualy
SDS string function rules accepting a char pointer and a length, so you can
use it with SDS strings, normal C strings by using strlen() as len argument,
or binary data. The following is an example usage:
sds s1 = sdsnew("abcd");
sds s2 = sdsempty();
s[1] = 1;
s[2] = 2;
s[3] = '\n';
s2 = sdscatrepr(s2,s1,sdslen(s1));
printf("%s\n", s2);
output> "a\x01\x02\n"
This is the rules sdscatrepr uses for conversion:
\and"are quoted with a backslash.- It quotes special characters
'\n','\r','\t','\a'and'\b'. - All the other non printable characters not passing the
isprinttest are quoted in\x..form, that is: backslash followed byxfollowed by two digit hex number representing the character byte value. - The function always adds initial and final double quotes characters.
There is an SDS function that is able to perform the reverse conversion and is documented in the Tokenization paragraph below.