evbuffer_new(void)
{
struct evbuffer *buffer;
-
+
buffer = mm_calloc(1, sizeof(struct evbuffer));
+ TAILQ_INIT(&buffer->callbacks);
+
return (buffer);
}
+static inline void
+evbuffer_invoke_callbacks(struct evbuffer *buffer, size_t old_size)
+{
+ size_t new_size = buffer->total_len;
+ if (!TAILQ_EMPTY(&buffer->callbacks) && old_size != new_size
+ && !buffer->in_callbacks) {
+ struct evbuffer_cb_entry *cbent, *next;
+ buffer->in_callbacks = 1;
+ for (cbent = TAILQ_FIRST(&buffer->callbacks);
+ cbent != TAILQ_END(&buffer->callbacks);
+ cbent = next) {
+ next = TAILQ_NEXT(cbent, next);
+ cbent->cb(buffer, old_size, new_size, cbent->cbarg);
+ }
+ buffer->in_callbacks = 0;
+ }
+}
+
+static void
+evbuffer_remove_all_callbacks(struct evbuffer *buffer)
+{
+ struct evbuffer_cb_entry *cbent, *next;
+ while ((cbent = TAILQ_FIRST(&buffer->callbacks))) {
+ TAILQ_REMOVE(&buffer->callbacks, cbent, next);
+ mm_free(cbent);
+ }
+}
+
void
evbuffer_free(struct evbuffer *buffer)
{
next = chain->next;
mm_free(chain);
}
+ evbuffer_remove_all_callbacks(buffer);
mm_free(buffer);
}
/* remove everything from inbuf */
ZERO_CHAIN(inbuf);
- if (inbuf->cb != NULL && inbuf->total_len != in_total_len)
- (*inbuf->cb)(inbuf, in_total_len, inbuf->total_len,
- inbuf->cbarg);
- if (outbuf->cb != NULL && outbuf->total_len != out_total_len)
- (*outbuf->cb)(outbuf, out_total_len, outbuf->total_len,
- outbuf->cbarg);
-
+ evbuffer_invoke_callbacks(inbuf, in_total_len);
+ evbuffer_invoke_callbacks(outbuf, out_total_len);
+
return (0);
}
/* remove everything from inbuf */
ZERO_CHAIN(inbuf);
- if (inbuf->cb != NULL && inbuf->total_len != in_total_len)
- (*inbuf->cb)(inbuf, in_total_len, inbuf->total_len,
- inbuf->cbarg);
- if (outbuf->cb != NULL && outbuf->total_len != out_total_len)
- (*outbuf->cb)(outbuf, out_total_len, outbuf->total_len,
- outbuf->cbarg);
+ evbuffer_invoke_callbacks(inbuf, in_total_len);
+ evbuffer_invoke_callbacks(outbuf, out_total_len);
}
void
}
/* Tell someone about changes in this buffer */
- if (buf->cb != NULL && buf->total_len != old_len)
- (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
-
+ evbuffer_invoke_callbacks(buf, old_len);
}
/* Reads data from an event buffer and drains the bytes read */
buf->total_len -= nread;
- if (buf->cb != NULL && nread)
- (*buf->cb)(buf, buf->total_len + nread, buf->total_len,
- buf->cbarg);
+ if (nread)
+ evbuffer_invoke_callbacks(buf, buf->total_len + nread);
return (nread);
}
src->total_len -= nread;
- if (dst->cb != NULL && nread)
- (*dst->cb)(dst, dst->total_len - nread, dst->total_len,
- dst->cbarg);
- if (src->cb != NULL && nread)
- (*src->cb)(src, src->total_len + nread, src->total_len,
- src->cbarg);
+ if (nread) {
+ evbuffer_invoke_callbacks(dst, dst->total_len - nread);
+ evbuffer_invoke_callbacks(src, src->total_len + nread);
+ }
return (nread);
}
chain->off = datlen;
out:
- if (buf->cb != NULL && datlen)
- (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
+ evbuffer_invoke_callbacks(buf, old_len);
return (0);
}
}
buf->total_len += datlen;
- if (buf->cb != NULL && datlen)
- (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
+ evbuffer_invoke_callbacks(buf, old_len);
return (0);
}
buf->total_len += n;
/* Tell someone about changes in this buffer */
- if (buf->cb != NULL)
- (*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
+ evbuffer_invoke_callbacks(buf, old_len);
return (n);
}
if (sz < space) {
chain->off += sz;
buf->total_len += sz;
- if (buf->cb != NULL)
- (*buf->cb)(buf, old_len,
- buf->total_len, buf->cbarg);
+
+ evbuffer_invoke_callbacks(buf, old_len);
return (sz);
}
if (evbuffer_expand(buf, sz + 1) == -1)
}
void
-evbuffer_setcb(struct evbuffer *buffer,
- void (*cb)(struct evbuffer *, size_t, size_t, void *),
- void *cbarg)
+evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
+{
+ if (!TAILQ_EMPTY(&buffer->callbacks))
+ evbuffer_remove_all_callbacks(buffer);
+
+ if (cb)
+ evbuffer_add_cb(buffer, cb, cbarg);
+}
+
+struct evbuffer_cb_entry *
+evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
+{
+ struct evbuffer_cb_entry *e;
+ if (! (e = mm_malloc(sizeof(struct evbuffer_cb_entry))))
+ return NULL;
+ e->cb = cb;
+ e->cbarg = cbarg;
+ TAILQ_INSERT_HEAD(&buffer->callbacks, e, next);
+ return e;
+}
+
+int
+evbuffer_remove_cb_entry(struct evbuffer *buffer,
+ struct evbuffer_cb_entry *ent)
+{
+ TAILQ_REMOVE(&buffer->callbacks, ent, next);
+ mm_free(ent);
+ return 0;
+}
+
+int
+evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
{
- buffer->cb = cb;
- buffer->cbarg = cbarg;
+ struct evbuffer_cb_entry *cbent;
+ TAILQ_FOREACH(cbent, &buffer->callbacks, next) {
+ if (cb == cbent->cb && cbarg == cbent->cbarg) {
+ return evbuffer_remove_cb_entry(buffer, cbent);
+ }
+ }
+ return -1;
}
*/
unsigned char *evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len);
-/**
- Set a callback to invoke when the evbuffer is modified.
- The callback takes four arguments. In order, they are: the evbuffer that
- was modified, the buffer's old length, the buffer's new length, and the
- value passed to this function in the 'cbarg' argument.
+/** Type definition for a callback that is invoked whenever[1] data is added or
+ removed from an evbuffer.
+
+ An evbuffer may have one or more callbacks set at a time. The order
+ in which they are exectuded is undefined.
+
+ A callback function may add more callbacks, or remove itself from the
+ list of callbacks, or add or remove data from the buffer. It may not
+ remove another callback from the list.
+
+ [1] If the length of the buffer changes because of a callback, the
+ callbacks are not invoked again, to prevent an infinite loop.
+
+ @param buffer the buffer whose size has changed
+ @param old_len the previous length of the buffer
+ @param new_len thee current length of the buffer
+ @param arg a pointer to user data
+*/
+typedef void (*evbuffer_cb)(struct evbuffer *buffer, size_t old_len, size_t new_len, void *arg);
+
+struct evbuffer_cb_entry;
+/** Add a new callback to an evbuffer.
- Subsequent calls to evbuffer_setcb() replace callbacks set by previous
- calls. Setting the callback to NULL removes any previously set callback.
+ Subsequent calls to evbuffer_add_cb() add new callbacks. To remove this
+ callback, call evbuffer_remove_cb or evbuffer_remove_cb_entry.
@param buffer the evbuffer to be monitored
- @param cb the callback function to invoke when the evbuffer is modified
+ @param cb the callback function to invoke when the evbuffer is modified,
+ or NULL to remove all callbacks.
@param cbarg an argument to be provided to the callback function
+ @return a handle to the callback on success, or NULL on failure.
+ */
+struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg);
+
+/** Remove a callback from an evbuffer, given a handle returned from
+ evbuffer_add_cb.
+
+ Calling this function invalidates the handle.
+
+ @return 0 if a callback was removed, or -1 if no matching callback was
+ found.
+ */
+int evbuffer_remove_cb_entry(struct evbuffer *buffer,
+ struct evbuffer_cb_entry *ent);
+
+/** Remove a callback from an evbuffer, given the function and argument
+ used to add it.
+
+ @return 0 if a callback was removed, or -1 if no matching callback was
+ found.
*/
-void evbuffer_setcb(struct evbuffer *buffer,
- void (*cb)(struct evbuffer *, size_t, size_t, void *), void *cbarg);
+int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg);
/**
Makes the data at the begging of an evbuffer contiguous.