HackingStrings.html 5.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
  2. <html>
  3. <head>
  4. <link type="text/css" rel="stylesheet" href="style.css" />
  5. </head>
  6. <body>
  7. <div id="page">
  8. <div id='header'>
  9. <a href="index.html">
  10. <img style="border:none" alt="Redis Documentation" src="redis.png">
  11. </a>
  12. </div>
  13. <div id="pagecontent">
  14. <div class="index">
  15. <!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
  16. <b>HackingStrings: Contents</b><br>&nbsp;&nbsp;<a href="#Hacking Strings">Hacking Strings</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Creating Redis Strings">Creating Redis Strings</a>
  17. </div>
  18. <h1 class="wikiname">HackingStrings</h1>
  19. <div class="summary">
  20. </div>
  21. <div class="narrow">
  22. <h1><a name="Hacking Strings">Hacking Strings</a></h1>The implementation of Redis strings is contained in <b></b>sds.c<b></b> ( sds stands for Simple Dynamic Strings ).<br/><br/>The C structure <i>sdshdr</i> declared in <b>sds.h</b> represents a Redis string:<br/><br/><pre class="codeblock python" name="code">
  23. struct sdshdr {
  24. long len;
  25. long free;
  26. char buf[];
  27. };
  28. </pre>The <i>buf</i> character array stores the actual string.<br/><br/>The <i>len</i> field stores the length of <i>buf</i>. This makes obtaining the length
  29. of a Redis string an O(1) operation.<br/><br/>The <i>free</i> field stores the number of additional bytes available for use.<br/><br/>Together the <i>len</i> and <i>free</i> field can be thought of as holding the metadata of the
  30. <i>buf</i> character array.<h2><a name="Creating Redis Strings">Creating Redis Strings</a></h2>A new data type named <code name="code" class="python">sds</code> is defined in <b>sds.h</b> to be a synonymn for a character pointer:<br/><br/><pre class="codeblock python python" name="code">
  31. typedef char *sds;
  32. </pre><code name="code" class="python">sdsnewlen</code> function defined in <b>sds.c</b> creates a new Redis String: <br/><br/><pre class="codeblock python python python" name="code">
  33. sds sdsnewlen(const void *init, size_t initlen) {
  34. struct sdshdr *sh;
  35. sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
  36. #ifdef SDS_ABORT_ON_OOM
  37. if (sh == NULL) sdsOomAbort();
  38. #else
  39. if (sh == NULL) return NULL;
  40. #endif
  41. sh-&gt;len = initlen;
  42. sh-&gt;free = 0;
  43. if (initlen) {
  44. if (init) memcpy(sh-&gt;buf, init, initlen);
  45. else memset(sh-&gt;buf,0,initlen);
  46. }
  47. sh-&gt;buf[initlen] = '\0';
  48. return (char*)sh-&gt;buf;
  49. }
  50. </pre>Remember a Redis string is a variable of type <code name="code" class="python">struct sdshdr</code>. But <code name="code" class="python">sdsnewlen</code> returns a character pointer!!<br/><br/>That's a trick and needs some explanation.<br/><br/>Suppose I create a Redis string using <code name="code" class="python">sdsnewlen</code> like below:<br/><br/><pre class="codeblock python python python python" name="code">
  51. sdsnewlen(&quot;redis&quot;, 5);
  52. </pre>This creates a new variable of type <code name="code" class="python">struct sdshdr</code> allocating memory for <i>len</i> and <i>free</i>
  53. fields as well as for the <i>buf</i> character array.<br/><br/><pre class="codeblock python python python python python" name="code">
  54. sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlen is length of init argument.
  55. </pre>After <code name="code" class="python">sdsnewlen</code> succesfully creates a Redis string the result is something like:<br/><br/><pre class="codeblock python python python python python python" name="code">
  56. -----------
  57. |5|0|redis|
  58. -----------
  59. ^ ^
  60. sh sh-&gt;buf
  61. </pre><code name="code" class="python">sdsnewlen</code> returns sh-&gt;buf to the caller.<br/><br/>What do you do if you need to free the Redis string pointed by <code name="code" class="python">sh</code>?<br/><br/>You want the pointer <code name="code" class="python">sh</code> but you only have the pointer <code name="code" class="python">sh-&gt;buf</code>.<br/><br/>Can you get the pointer <code name="code" class="python">sh</code> from <code name="code" class="python">sh-&gt;buf</code>?<br/><br/>Yes. Pointer arithmetic. Notice from the above ASCII art that if you subtract
  62. the size of two longs from <code name="code" class="python">sh-&gt;buf</code> you get the pointer <code name="code" class="python">sh</code>. <br/><br/>The sizeof two longs happens to be the size of <code name="code" class="python">struct sdshdr</code>.<br/><br/>Look at <code name="code" class="python">sdslen</code> function and see this trick at work:<br/><br/><pre class="codeblock python python python python python python python" name="code">
  63. size_t sdslen(const sds s) {
  64. struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
  65. return sh-&gt;len;
  66. }
  67. </pre>Knowing this trick you could easily go through the rest of the functions in <b>sds.c</b>.<br/><br/>The Redis string implementation is hidden behind an interface that accepts only character pointers. The users of Redis strings need not care about how its implemented and treat Redis strings as a character pointer.
  68. </div>
  69. </div>
  70. </div>
  71. </body>
  72. </html>