]>
git.saurik.com Git - redis.git/blob - ziplist.c
1 /* Memory layout of a ziplist, containing "foo", "bar", "quux":
2 * <zlbytes><zllen><len>"foo"<len>"bar"<len>"quux"
4 * <zlbytes> is an unsigned integer to hold the number of bytes that
5 * the ziplist occupies. This is stored to not have to traverse the ziplist
6 * to know the new length when pushing.
8 * <zllen> is the number of items in the ziplist. When this value is
9 * greater than 254, we need to traverse the entire list to know
10 * how many items it holds.
12 * <len> is the number of bytes occupied by a single entry. When this
13 * number is greater than 253, the length will occupy 5 bytes, where
14 * the extra bytes contain an unsigned integer to hold the length.
25 #define ZIPLIST_BYTES(zl) (*((unsigned int*)(zl)))
26 #define ZIPLIST_LENGTH(zl) (*((zl)+sizeof(unsigned int)))
27 #define ZIPLIST_HEADER_SIZE (sizeof(unsigned int)+1)
29 /* Create a new empty ziplist. */
30 unsigned char *ziplistNew(void) {
31 unsigned int bytes
= ZIPLIST_HEADER_SIZE
+1;
32 unsigned char *zl
= zmalloc(bytes
);
33 ZIPLIST_BYTES(zl
) = bytes
;
34 ZIPLIST_LENGTH(zl
) = 0;
35 zl
[bytes
-1] = ZIP_END
;
39 static unsigned char *ziplistResize(unsigned char *zl
, unsigned int len
) {
40 zl
= zipResize(zl
,len
);
41 ZIPLIST_BYTES(zl
) = len
;
46 static unsigned char *ziplistHead(unsigned char *zl
) {
47 return zl
+ZIPLIST_HEADER_SIZE
;
50 static unsigned char *ziplistTail(unsigned char *zl
) {
52 p
= q
= ziplistHead(zl
);
53 while (*p
!= ZIP_END
) {
55 p
+= zipRawEntryLength(p
);
60 unsigned char *ziplistPush(unsigned char *zl
, unsigned char *entry
, unsigned int elen
, int where
) {
61 unsigned int curlen
= ZIPLIST_BYTES(zl
);
62 unsigned int reqlen
= zipEncodeLength(NULL
,elen
)+elen
;
65 /* Resize the ziplist */
66 zl
= ziplistResize(zl
,curlen
+reqlen
);
68 if (where
== ZIPLIST_HEAD
) {
69 p
= zl
+ZIPLIST_HEADER_SIZE
;
71 /* Subtract one because of the ZIP_END bytes */
72 memmove(p
+reqlen
,p
,curlen
-ZIPLIST_HEADER_SIZE
-1);
79 if (ZIPLIST_LENGTH(zl
) < ZIP_BIGLEN
) ZIPLIST_LENGTH(zl
)++;
82 p
+= zipEncodeLength(p
,elen
);
87 unsigned char *ziplistPop(unsigned char *zl
, sds
*value
, int where
) {
88 unsigned int curlen
= ZIPLIST_BYTES(zl
), len
, rlen
;
92 /* Get pointer to element to remove */
93 p
= (where
== ZIPLIST_HEAD
) ? ziplistHead(zl
) : ziplistTail(zl
);
94 if (*p
== ZIP_END
) return zl
;
95 len
= zipDecodeLength(p
);
96 *value
= sdsnewlen(p
+zipEncodeLength(NULL
,len
),len
);
98 /* Move list to front when popping from the head */
99 rlen
= zipRawEntryLength(p
);
100 if (where
== ZIPLIST_HEAD
) {
101 memmove(p
,p
+rlen
,curlen
-ZIPLIST_HEADER_SIZE
-len
);
104 /* Resize and update length */
105 zl
= ziplistResize(zl
,curlen
-rlen
);
106 if (ZIPLIST_LENGTH(zl
) < ZIP_BIGLEN
) ZIPLIST_LENGTH(zl
)--;
110 void ziplistRepr(unsigned char *zl
) {
114 printf("{bytes %d} {length %u}\n",ZIPLIST_BYTES(zl
), ZIPLIST_LENGTH(zl
));
116 while(*p
!= ZIP_END
) {
117 l
= zipDecodeLength(p
);
118 printf("{key %u}",l
);
119 p
+= zipEncodeLength(NULL
,l
);
120 fwrite(p
,l
,1,stdout
);
127 #ifdef ZIPLIST_TEST_MAIN
128 int main(int argc
, char **argv
) {
133 zl
= ziplistPush(zl
, (unsigned char*)"foo", 3, ZIPLIST_TAIL
);
135 zl
= ziplistPush(zl
, (unsigned char*)"quux", 4, ZIPLIST_TAIL
);
137 zl
= ziplistPush(zl
, (unsigned char*)"hello", 5, ZIPLIST_HEAD
);
140 zl
= ziplistPop(zl
, &s
, ZIPLIST_TAIL
);
141 printf("Pop tail: %s (length %ld)\n", s
, sdslen(s
));
144 zl
= ziplistPop(zl
, &s
, ZIPLIST_HEAD
);
145 printf("Pop head: %s (length %ld)\n", s
, sdslen(s
));