/** * Discard `n_bytes' from the pmsg_t buffer slist and free all completely * discarded buffers. */ void pmsg_slist_discard(slist_t *slist, size_t n_bytes) { slist_iter_t *iter; g_assert(slist); iter = slist_iter_removable_on_head(slist); while (n_bytes > 0) { pmsg_t *mb; size_t size; g_assert(slist_iter_has_item(iter)); mb = slist_iter_current(iter); pmsg_check_consistency(mb); size = pmsg_size(mb); if (size > n_bytes) { pmsg_discard(mb, n_bytes); break; } else { pmsg_free(mb); n_bytes -= size; slist_iter_remove(iter); } } slist_iter_free(&iter); }
/** * Set the pre-send hook routine for the buffer. * * This routine, if it exists (non-NULL) is called just before sending * the message at the lowest level. If it returns FALSE, the message is * immediately dropped. * * The callback routine must not modify the message. * * The difference with a "check callback" is that a hook is only invoked * on the standalone buffer, the layer perusing this information being * able to gather all its context from the message, using its protocol header * relevant to the layer. */ void pmsg_set_hook(pmsg_t *mb, pmsg_hook_t hook) { pmsg_check_consistency(mb); mb->m_u.m_hook = hook; mb->m_flags |= PMSG_PF_HOOK; /* Is a hook */ }
/** * Set the pre-send checking routine for the buffer. * * This routine, if it exists (non-NULL) is called just before enqueueing * the message for sending. If it returns FALSE, the message is immediately * dropped. * * The callback routine must not modify the message, as the buffer can * be shared among multiple messages, unless its refcount is 1. */ void pmsg_set_check(pmsg_t *mb, pmsg_check_t check) { pmsg_check_consistency(mb); mb->m_u.m_check = check; mb->m_flags &= ~PMSG_PF_HOOK; /* Is not a hook */ }
/** * Reset message block, discarding all the data buffered and restoring the * state it had after creation. Upon return, it can be used as if a brand * new message block had been created. */ void pmsg_reset(pmsg_t *mb) { pmsg_check_consistency(mb); mb->m_rptr = mb->m_wptr = mb->m_data->d_arena; /* Empty buffer */ mb->m_flags = PMSG_EXT_MAGIC == mb->magic ? PMSG_PF_EXT : 0; mb->m_u.m_check = NULL; /* Clear "pre-send" checks */ }
/** * Copy ``len'' bytes from the source message block to the destination by * reading the source bytes and writing them to the recipient. * * @returns amount of bytes written, which may be lower than the requested * amount if the source buffer was shorter or there is not enough room in * the destination. */ int pmsg_copy(pmsg_t *dest, pmsg_t *src, int len) { int copied, available; pmsg_check_consistency(dest); pmsg_check_consistency(src); g_assert(pmsg_is_writable(dest)); /* Not shared, or would corrupt data */ copied = src->m_wptr - src->m_rptr; /* Available data in source */ copied = MIN(copied, len); available = pmsg_available(dest); /* Room in destination buffer */ copied = MIN(copied, available); if (copied > 0) { dest->m_wptr = mempcpy(dest->m_wptr, src->m_rptr, copied); src->m_rptr += copied; } return copied; }
/** * Increase the reference count on the message block. * * This must be used in TX stacks when there is a free routine installed * on messages and we want to keep another reference to the message, yet * allow upper layers to pmsg_free() the message block as if it had been * sent from their point of view. * * It also allows correct pmsg_was_sent() checks in free routines, whereas * a pmsg_clone() would create a new message. * * @return its argument, for convenience. */ pmsg_t * pmsg_ref(pmsg_t *mb) { pmsg_check_consistency(mb); g_assert(mb->m_refcnt != 0); mb->m_refcnt++; g_assert(mb->m_refcnt != 0); /* Safeguard against overflows */ return mb; }
/** * Shallow cloning of message, making sure we have a plain clone even if * the original was extended. */ pmsg_t * pmsg_clone_plain(const pmsg_t *mb) { pmsg_t *nmb; pmsg_check_consistency(mb); WALLOC(nmb); memcpy(nmb, mb, sizeof *nmb); nmb->magic = PMSG_MAGIC; /* Force plain message */ nmb->m_flags &= ~PMSG_PF_EXT; /* In case original was extended */ nmb->m_refcnt = 1; pdata_addref(nmb->m_data); return nmb; }
/** * Read data from the message, returning the amount of bytes transferred. */ int pmsg_read(pmsg_t *mb, void *data, int len) { int available, readable; pmsg_check_consistency(mb); available = mb->m_wptr - mb->m_rptr; g_assert(available >= 0); /* Data cannot go beyond end of arena */ readable = len >= available ? available : len; if (readable != 0) { memcpy(data, mb->m_rptr, readable); mb->m_rptr += readable; } return readable; }
/** * @return amount of data that can be written at the end of the message. */ int pmsg_writable_length(const pmsg_t *mb) { pdata_t *arena; int available; pmsg_check_consistency(mb); /* * If buffer is not writable (shared among several readers), it is * forbidden to write any new data to it. */ arena = mb->m_data; available = arena->d_end - mb->m_wptr; return pmsg_is_writable(mb) ? available : 0; }
/** * Shallow cloning of message, result is referencing the same data. * * This is not the same thing as pmsg_ref() because here a new message block * is created (albeit the data are shared with the original message). */ pmsg_t * pmsg_clone(pmsg_t *mb) { if (pmsg_is_extended(mb)) { return pmsg_clone_ext(cast_to_pmsg_ext(mb)); } else { pmsg_t *nmb; pmsg_check_consistency(mb); WALLOC(nmb); *nmb = *mb; /* Struct copy */ nmb->m_refcnt = 1; pdata_addref(nmb->m_data); return nmb; } }
/** * Discard trailing data from the message, returning the amount of * bytes discarded. */ int pmsg_discard_trailing(pmsg_t *mb, int len) { int available, n; pmsg_check_consistency(mb); available = mb->m_wptr - mb->m_rptr; g_assert(available >= 0); /* Data cannot go beyond end of arena */ /* * The write pointer moves backward to point before the discarded bytes. */ n = len >= available ? available : len; mb->m_wptr -= n; return n; }
/** * Discard data from the message, returning the amount of bytes discarded. */ int pmsg_discard(pmsg_t *mb, int len) { int available, n; pmsg_check_consistency(mb); available = mb->m_wptr - mb->m_rptr; g_assert(available >= 0); /* Data cannot go beyond end of arena */ /* * The read pointer moves forward to point after the discarded bytes. */ n = len >= available ? available : len; mb->m_rptr += n; return n; }
/** * Split a buffer at given offset: the data before that offset are left in * the original buffer whilst the data starting at the offset (included) * are moved to a new buffer. The original buffer no longer holds the data * starting at the offset. * * @return new message block containing the data starting at the offset. */ pmsg_t * pmsg_split(pmsg_t *mb, int offset) { int slen; /* Split length */ const char *start; g_assert(offset >= 0); g_assert(offset < pmsg_size(mb)); pmsg_check_consistency(mb); start = mb->m_rptr + offset; slen = mb->m_wptr - start; g_assert(slen > 0); mb->m_wptr -= slen; /* Logically removed */ return pmsg_new(mb->m_prio, start, slen); /* Copies data */ }
/** * Shift back unread data to the beginning of the buffer. */ void pmsg_compact(pmsg_t *mb) { int shifting; pmsg_check_consistency(mb); g_assert(pmsg_is_writable(mb)); /* Not shared, or would corrupt data */ g_assert(mb->m_rptr <= mb->m_wptr); shifting = mb->m_rptr - mb->m_data->d_arena; g_assert(shifting >= 0); if (shifting != 0) { memmove(mb->m_data->d_arena, mb->m_rptr, pmsg_size(mb)); mb->m_rptr -= shifting; mb->m_wptr -= shifting; } }
/** * Creates an iovec from a singly-linked list of pmsg_t buffers. * It should be freed via hfree(). * * NOTE: The iovec will hold no more than MAX_IOV_COUNT items. That means * the iovec might not cover the whole buffered data. This limit * is applied because writev() could fail with EINVAL otherwise * which would simply add more unnecessary complexity. */ iovec_t * pmsg_slist_to_iovec(slist_t *slist, int *iovcnt_ptr, size_t *size_ptr) { iovec_t *iov; size_t held = 0; int n; g_assert(slist); n = slist_length(slist); if (n > 0) { slist_iter_t *iter; int i; n = MIN(n, MAX_IOV_COUNT); iov = halloc(n * sizeof *iov); iter = slist_iter_before_head(slist); for (i = 0; i < n; i++) { pmsg_t *mb; size_t size; mb = slist_iter_next(iter); pmsg_check_consistency(mb); size = pmsg_size(mb); g_assert(size > 0); held += size; iovec_set(&iov[i], deconstify_pointer(pmsg_read_base(mb)), size); } slist_iter_free(&iter); } else { iov = NULL; } if (iovcnt_ptr) { *iovcnt_ptr = MAX(0, n); } if (size_ptr) { *size_ptr = held; } return iov; }
/** * Write data at the end of the message. * The message must be the only reference to the underlying data. * * @returns amount of written data. */ int pmsg_write(pmsg_t *mb, const void *data, int len) { pdata_t *arena; int available, written; pmsg_check_consistency(mb); g_assert(pmsg_is_writable(mb)); /* Not shared, or would corrupt data */ arena = mb->m_data; available = arena->d_end - mb->m_wptr; g_assert(available >= 0); /* Data cannot go beyond end of arena */ written = len >= available ? available : len; if (written != 0) mb->m_wptr = mempcpy(mb->m_wptr, data, written); return written; }
/** * Extended cloning of message, adds a free routine callback. */ pmsg_t * pmsg_clone_extend(pmsg_t *mb, pmsg_free_t free_cb, void *arg) { pmsg_ext_t *nmb; pmsg_check_consistency(mb); WALLOC(nmb); nmb->pmsg = *mb; /* Struct copy */ nmb->pmsg.magic = PMSG_EXT_MAGIC; pdata_addref(nmb->pmsg.m_data); nmb->pmsg.m_flags |= PMSG_PF_EXT; nmb->pmsg.m_refcnt = 1; nmb->m_free = free_cb; nmb->m_arg = arg; return cast_to_pmsg(nmb); }
/** * Returns the size of the data held in the buffer list. */ size_t pmsg_slist_size(const slist_t *slist) { slist_iter_t *iter; size_t size = 0; g_assert(slist != NULL); iter = slist_iter_before_head(slist); while (slist_iter_has_next(iter)) { const pmsg_t *mb; mb = slist_iter_next(iter); pmsg_check_consistency(mb); size += pmsg_size(mb); } slist_iter_free(&iter); return size; }
/** * Shift back unread data to the beginning of the buffer if that can make * at least 1/nth of the total arena size available for writing. */ void pmsg_fractional_compact(pmsg_t *mb, int n) { int shifting; g_assert(n > 0); pmsg_check_consistency(mb); g_assert(pmsg_is_writable(mb)); /* Not shared, or would corrupt data */ g_assert(mb->m_rptr <= mb->m_wptr); shifting = mb->m_rptr - mb->m_data->d_arena; g_assert(shifting >= 0); if (shifting != 0) { unsigned available = pmsg_available(mb) + shifting; if (available >= pmsg_phys_len(mb) / n) { memmove(mb->m_data->d_arena, mb->m_rptr, pmsg_size(mb)); mb->m_rptr -= shifting; mb->m_wptr -= shifting; } } }
/** * Free all message blocks, and decrease ref count on all data buffers. * * If the message block is referenced by more than one place, simply * decrease its reference count. No freeing occurs and the free routine * is therefore not invoked. */ void pmsg_free(pmsg_t *mb) { pdata_t *db = mb->m_data; pmsg_check_consistency(mb); g_assert(mb->m_refcnt != 0); /* * Don't free anything if refcnt != 1. */ if (mb->m_refcnt > 1U) { mb->m_refcnt--; return; } /* * Invoke free routine on extended message block. */ if (pmsg_is_extended(mb)) { pmsg_ext_t *emb = cast_to_pmsg_ext(mb); if (emb->m_free) (*emb->m_free)(mb, emb->m_arg); WFREE0(emb); } else { WFREE0(mb); } /* * Unref buffer data only after possible free routine was * invoked, since it may cause a free, preventing access to * memory from within the free routine. */ pdata_unref(db); }
/** * Fill newly created message block. * * @return the message block given as argument. */ static pmsg_t * pmsg_fill(pmsg_t *mb, pdata_t *db, int prio, bool ext, const void *buf, int len) { mb->magic = ext ? PMSG_EXT_MAGIC : PMSG_MAGIC; mb->m_data = db; mb->m_prio = prio; mb->m_flags = ext ? PMSG_PF_EXT : 0; mb->m_u.m_check = NULL; mb->m_refcnt = 1; db->d_refcnt++; if (buf) { mb->m_rptr = db->d_arena; mb->m_wptr = db->d_arena + len; memcpy(db->d_arena, buf, len); } else mb->m_rptr = mb->m_wptr = db->d_arena; g_assert(implies(buf, len == pmsg_size(mb))); pmsg_check_consistency(mb); return mb; }