+/* When an entry is inserted, we need to set the prevlen field of the next
+ * entry to equal the length of the inserted entry. It can occur that this
+ * length cannot be encoded in 1 byte and the next entry needs to be grow
+ * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,
+ * because this only happens when an entry is already being inserted (which
+ * causes a realloc and memmove). However, encoding the prevlen may require
+ * that this entry is grown as well. This effect may cascade throughout
+ * the ziplist when there are consecutive entries with a size close to
+ * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every
+ * consecutive entry.
+ *
+ * Note that this effect can also happen in reverse, where the bytes required
+ * to encode the prevlen field can shrink. This effect is deliberately ignored,
+ * because it can cause a "flapping" effect where a chain prevlen fields is
+ * first grown and then shrunk again after consecutive inserts. Rather, the
+ * field is allowed to stay larger than necessary, because a large prevlen
+ * field implies the ziplist is holding large entries anyway.
+ *
+ * The pointer "p" points to the first entry that does NOT need to be
+ * updated, i.e. consecutive fields MAY need an update. */
+static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
+ size_t curlen = ZIPLIST_BYTES(zl), rawlen, rawlensize;
+ size_t offset, noffset, extra;
+ unsigned char *np;
+ zlentry cur, next;
+
+ while (p[0] != ZIP_END) {
+ cur = zipEntry(p);
+ rawlen = cur.headersize + cur.len;
+ rawlensize = zipPrevEncodeLength(NULL,rawlen);
+
+ /* Abort if there is no next entry. */
+ if (p[rawlen] == ZIP_END) break;
+ next = zipEntry(p+rawlen);
+
+ /* Abort when "prevlen" has not changed. */
+ if (next.prevrawlen == rawlen) break;
+
+ if (next.prevrawlensize < rawlensize) {
+ /* The "prevlen" field of "next" needs more bytes to hold
+ * the raw length of "cur". */
+ offset = p-zl;
+ extra = rawlensize-next.prevrawlensize;
+ zl = ziplistResize(zl,curlen+extra);
+ p = zl+offset;
+
+ /* Current pointer and offset for next element. */
+ np = p+rawlen;
+ noffset = np-zl;
+
+ /* Update tail offset when next element is not the tail element. */
+ if ((zl+ZIPLIST_TAIL_OFFSET(zl)) != np)
+ ZIPLIST_TAIL_OFFSET(zl) += extra;
+
+ /* Move the tail to the back. */
+ memmove(np+rawlensize,
+ np+next.prevrawlensize,
+ curlen-noffset-next.prevrawlensize-1);
+ zipPrevEncodeLength(np,rawlen);
+
+ /* Advance the cursor */
+ p += rawlen;
+ curlen += extra;
+ } else {
+ if (next.prevrawlensize > rawlensize) {
+ /* This would result in shrinking, which we want to avoid.
+ * So, set "rawlen" in the available bytes. */
+ zipPrevEncodeLengthForceLarge(p+rawlen,rawlen);
+ } else {
+ zipPrevEncodeLength(p+rawlen,rawlen);
+ }
+
+ /* Stop here, as the raw length of "next" has not changed. */
+ break;
+ }
+ }
+ return zl;
+}
+