]>
git.saurik.com Git - redis.git/blob - ziplist.c
5c8221876a7497e02ccb0bfe6e494c3a272e2759
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
;
90 if (value
) *value
= NULL
;
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 if (value
) *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
, unsigned char **entry
, unsigned int *elen
) {
124 if (*p
== ZIP_END
) return NULL
;
126 *elen
= zipDecodeLength(p
);
127 *entry
= p
+ZIP_LEN_BYTES(*elen
);
129 p
+= zipRawEntryLength(p
);
133 /* Delete one or more entries from the ziplist. */
134 unsigned char *ziplistDelete(unsigned char *zl
, unsigned int index
, unsigned int num
) {
135 unsigned char *p
, *first
= ziplistIndex(zl
, index
);
136 unsigned int i
, deleted
= 0, totlen
, newlen
;
137 for (p
= first
, i
= 0; *p
!= ZIP_END
&& i
< num
; i
++) {
138 p
+= zipRawEntryLength(p
);
144 /* Move current tail to the new tail when there *is* a tail */
145 if (*p
!= ZIP_END
) memmove(first
,p
,ZIPLIST_BYTES(zl
)-(p
-zl
)-1);
147 /* Resize and update length */
148 zl
= ziplistResize(zl
, ZIPLIST_BYTES(zl
)-totlen
);
149 if (ZIPLIST_LENGTH(zl
) < ZIP_BIGLEN
) ZIPLIST_LENGTH(zl
) -= deleted
;
154 void ziplistRepr(unsigned char *zl
) {
158 printf("{bytes %d} {length %u}\n",ZIPLIST_BYTES(zl
), ZIPLIST_LENGTH(zl
));
160 while(*p
!= ZIP_END
) {
161 l
= zipDecodeLength(p
);
162 printf("{key %u}",l
);
163 p
+= zipEncodeLength(NULL
,l
);
164 fwrite(p
,l
,1,stdout
);
171 #ifdef ZIPLIST_TEST_MAIN
173 unsigned char *createList() {
174 unsigned char *zl
= ziplistNew();
175 zl
= ziplistPush(zl
, (unsigned char*)"foo", 3, ZIPLIST_TAIL
);
176 zl
= ziplistPush(zl
, (unsigned char*)"quux", 4, ZIPLIST_TAIL
);
177 zl
= ziplistPush(zl
, (unsigned char*)"hello", 5, ZIPLIST_HEAD
);
181 int main(int argc
, char **argv
) {
182 unsigned char *zl
, *p
, *entry
;
189 zl
= ziplistPop(zl
, &s
, ZIPLIST_TAIL
);
190 printf("Pop tail: %s (length %ld)\n", s
, sdslen(s
));
193 zl
= ziplistPop(zl
, &s
, ZIPLIST_HEAD
);
194 printf("Pop head: %s (length %ld)\n", s
, sdslen(s
));
197 printf("Iterate list from 0 to end:\n");
200 p
= ziplistIndex(zl
, 0);
201 while ((p
= ziplistNext(p
, &entry
, &elen
)) != NULL
) {
203 fwrite(entry
,elen
,1,stdout
);
204 printf(" (length %d)\n", elen
);
209 printf("Iterate list from 1 to end:\n");
212 p
= ziplistIndex(zl
, 1);
213 while ((p
= ziplistNext(p
, &entry
, &elen
)) != NULL
) {
215 fwrite(entry
,elen
,1,stdout
);
216 printf(" (length %d)\n", elen
);
221 printf("Iterate list from 2 to end:\n");
224 p
= ziplistIndex(zl
, 2);
225 while ((p
= ziplistNext(p
, &entry
, &elen
)) != NULL
) {
227 fwrite(entry
,elen
,1,stdout
);
228 printf(" (length %d)\n", elen
);
233 printf("Iterate starting out of range:\n");
236 p
= ziplistIndex(zl
, 3);
237 if (ziplistNext(p
, &entry
, &elen
) == NULL
) {
238 printf("No entry\n");
245 printf("Delete inclusive range 0,0:\n");
248 zl
= ziplistDelete(zl
, 0, 1);
252 printf("Delete inclusive range 0,1:\n");
255 zl
= ziplistDelete(zl
, 0, 2);
259 printf("Delete inclusive range 1,2:\n");
262 zl
= ziplistDelete(zl
, 1, 2);
266 printf("Delete with start index out of range:\n");
269 zl
= ziplistDelete(zl
, 5, 1);
273 printf("Delete with num overflow:\n");
276 zl
= ziplistDelete(zl
, 1, 5);