]>
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 /* Returns an offset to use for iterating with ziplistNext. */
111 unsigned char *ziplistIndex(unsigned char *zl
, unsigned int index
) {
112 unsigned char *p
= zl
+ZIPLIST_HEADER_SIZE
;
114 for (; i
< index
; i
++) {
115 if (*p
== ZIP_END
) break;
116 p
+= zipRawEntryLength(p
);
121 /* Store entry at current position in sds *value and return pointer
122 * to the next entry. */
123 unsigned char *ziplistNext(unsigned char *p
, sds
*value
) {
124 if (*p
== ZIP_END
) return NULL
;
127 len
= zipDecodeLength(p
);
128 *value
= sdsnewlen(p
+zipEncodeLength(NULL
,len
),len
);
130 p
+= zipRawEntryLength(p
);
134 void ziplistRepr(unsigned char *zl
) {
138 printf("{bytes %d} {length %u}\n",ZIPLIST_BYTES(zl
), ZIPLIST_LENGTH(zl
));
140 while(*p
!= ZIP_END
) {
141 l
= zipDecodeLength(p
);
142 printf("{key %u}",l
);
143 p
+= zipEncodeLength(NULL
,l
);
144 fwrite(p
,l
,1,stdout
);
151 #ifdef ZIPLIST_TEST_MAIN
153 unsigned char *createList() {
154 unsigned char *zl
= ziplistNew();
155 zl
= ziplistPush(zl
, (unsigned char*)"foo", 3, ZIPLIST_TAIL
);
156 zl
= ziplistPush(zl
, (unsigned char*)"quux", 4, ZIPLIST_TAIL
);
157 zl
= ziplistPush(zl
, (unsigned char*)"hello", 5, ZIPLIST_HEAD
);
161 int main(int argc
, char **argv
) {
162 unsigned char *zl
, *p
;
168 zl
= ziplistPop(zl
, &s
, ZIPLIST_TAIL
);
169 printf("Pop tail: %s (length %ld)\n", s
, sdslen(s
));
172 zl
= ziplistPop(zl
, &s
, ZIPLIST_HEAD
);
173 printf("Pop head: %s (length %ld)\n", s
, sdslen(s
));
176 printf("Iterate list from 0 to end:\n");
179 p
= ziplistIndex(zl
, 0);
180 while ((p
= ziplistNext(p
, &s
)) != NULL
) {
181 printf("Entry: %s (length %ld)\n", s
, sdslen(s
));
186 printf("Iterate list from 1 to end:\n");
189 p
= ziplistIndex(zl
, 1);
190 while ((p
= ziplistNext(p
, &s
)) != NULL
) {
191 printf("Entry: %s (length %ld)\n", s
, sdslen(s
));
196 printf("Iterate list from 2 to end:\n");
199 p
= ziplistIndex(zl
, 2);
200 while ((p
= ziplistNext(p
, &s
)) != NULL
) {
201 printf("Entry: %s (length %ld)\n", s
, sdslen(s
));
206 printf("Iterate starting out of range:\n");
209 p
= ziplistIndex(zl
, 3);
210 if (ziplistNext(p
, &s
) == NULL
) {
211 printf("No entry\n");