]>
git.saurik.com Git - redis.git/blob - ziplist.c
0987bcca62a392d673781379b153028fd65926b6
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 **q
, 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 if (q
!= NULL
) *q
= p
;
130 p
+= zipRawEntryLength(p
);
134 /* Delete a range of entries from the ziplist. */
135 unsigned char *ziplistDeleteRange(unsigned char *zl
, unsigned int index
, unsigned int num
) {
136 unsigned char *p
, *first
= ziplistIndex(zl
, index
);
137 unsigned int i
, deleted
= 0, totlen
, newlen
;
138 for (p
= first
, i
= 0; *p
!= ZIP_END
&& i
< num
; i
++) {
139 p
+= zipRawEntryLength(p
);
145 /* Move current tail to the new tail when there *is* a tail */
146 if (*p
!= ZIP_END
) memmove(first
,p
,ZIPLIST_BYTES(zl
)-(p
-zl
)-1);
148 /* Resize and update length */
149 zl
= ziplistResize(zl
, ZIPLIST_BYTES(zl
)-totlen
);
150 if (ZIPLIST_LENGTH(zl
) < ZIP_BIGLEN
) ZIPLIST_LENGTH(zl
) -= deleted
;
155 void ziplistRepr(unsigned char *zl
) {
159 printf("{bytes %d} {length %u}\n",ZIPLIST_BYTES(zl
), ZIPLIST_LENGTH(zl
));
161 while(*p
!= ZIP_END
) {
162 l
= zipDecodeLength(p
);
163 printf("{key %u}",l
);
164 p
+= zipEncodeLength(NULL
,l
);
165 fwrite(p
,l
,1,stdout
);
172 #ifdef ZIPLIST_TEST_MAIN
174 unsigned char *createList() {
175 unsigned char *zl
= ziplistNew();
176 zl
= ziplistPush(zl
, (unsigned char*)"foo", 3, ZIPLIST_TAIL
);
177 zl
= ziplistPush(zl
, (unsigned char*)"quux", 4, ZIPLIST_TAIL
);
178 zl
= ziplistPush(zl
, (unsigned char*)"hello", 5, ZIPLIST_HEAD
);
182 int main(int argc
, char **argv
) {
183 unsigned char *zl
, *p
, *entry
;
190 zl
= ziplistPop(zl
, &s
, ZIPLIST_TAIL
);
191 printf("Pop tail: %s (length %ld)\n", s
, sdslen(s
));
194 zl
= ziplistPop(zl
, &s
, ZIPLIST_HEAD
);
195 printf("Pop head: %s (length %ld)\n", s
, sdslen(s
));
198 printf("Iterate list from 0 to end:\n");
201 p
= ziplistIndex(zl
, 0);
202 while ((p
= ziplistNext(p
, NULL
, &entry
, &elen
)) != NULL
) {
204 fwrite(entry
,elen
,1,stdout
);
205 printf(" (length %d)\n", elen
);
210 printf("Iterate list from 1 to end:\n");
213 p
= ziplistIndex(zl
, 1);
214 while ((p
= ziplistNext(p
, NULL
, &entry
, &elen
)) != NULL
) {
216 fwrite(entry
,elen
,1,stdout
);
217 printf(" (length %d)\n", elen
);
222 printf("Iterate list from 2 to end:\n");
225 p
= ziplistIndex(zl
, 2);
226 while ((p
= ziplistNext(p
, NULL
, &entry
, &elen
)) != NULL
) {
228 fwrite(entry
,elen
,1,stdout
);
229 printf(" (length %d)\n", elen
);
234 printf("Iterate starting out of range:\n");
237 p
= ziplistIndex(zl
, 3);
238 if (ziplistNext(p
, &entry
, NULL
, &elen
) == NULL
) {
239 printf("No entry\n");
246 printf("Delete inclusive range 0,0:\n");
249 zl
= ziplistDeleteRange(zl
, 0, 1);
253 printf("Delete inclusive range 0,1:\n");
256 zl
= ziplistDeleteRange(zl
, 0, 2);
260 printf("Delete inclusive range 1,2:\n");
263 zl
= ziplistDeleteRange(zl
, 1, 2);
267 printf("Delete with start index out of range:\n");
270 zl
= ziplistDeleteRange(zl
, 5, 1);
274 printf("Delete with num overflow:\n");
277 zl
= ziplistDeleteRange(zl
, 1, 5);