chainbuffer fixed
This commit is contained in:
@@ -1,48 +1,245 @@
|
||||
#include "network/chainbuffer.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define CHAINBUFFER_DEFAULT_CHUNK 4096
|
||||
#define CHAINBUFFER_MAX_IOV 16
|
||||
#define CHAINBUFFER_DEFAULT_FREE_LIMIT 256
|
||||
|
||||
struct chain_buffer_node {
|
||||
struct chain_buffer_node *next;
|
||||
size_t start;
|
||||
size_t end;
|
||||
size_t cap;
|
||||
size_t rpos;
|
||||
size_t wpos;
|
||||
size_t used;
|
||||
unsigned refcnt;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
static chain_buffer_node_t *alloc_node(size_t cap) {
|
||||
chain_buffer_node_t *node = (chain_buffer_node_t *)malloc(sizeof(*node) + cap);
|
||||
chain_buffer_node_t *node;
|
||||
|
||||
node = (chain_buffer_node_t *)malloc(sizeof(*node) + cap);
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->next = NULL;
|
||||
node->start = 0;
|
||||
node->end = 0;
|
||||
node->cap = cap;
|
||||
node->rpos = 0;
|
||||
node->wpos = 0;
|
||||
node->used = 0;
|
||||
node->refcnt = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
static void reset_node_state(chain_buffer_node_t *node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
node->next = NULL;
|
||||
node->rpos = 0;
|
||||
node->wpos = 0;
|
||||
node->used = 0;
|
||||
node->refcnt = 0;
|
||||
}
|
||||
|
||||
static size_t min_size(size_t a, size_t b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static size_t node_read_seg1_len(const chain_buffer_node_t *node) {
|
||||
size_t tail_len;
|
||||
|
||||
if (!node || node->used == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tail_len = node->cap - node->rpos;
|
||||
return min_size(node->used, tail_len);
|
||||
}
|
||||
|
||||
static size_t node_write_seg1_len(const chain_buffer_node_t *node) {
|
||||
size_t free_total;
|
||||
size_t tail_len;
|
||||
|
||||
if (!node || node->used >= node->cap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_total = node->cap - node->used;
|
||||
tail_len = node->cap - node->wpos;
|
||||
return min_size(free_total, tail_len);
|
||||
}
|
||||
|
||||
static void node_read_segments(const chain_buffer_node_t *node,
|
||||
uint8_t **p1, size_t *l1,
|
||||
uint8_t **p2, size_t *l2) {
|
||||
size_t seg1;
|
||||
size_t seg2;
|
||||
|
||||
seg1 = node_read_seg1_len(node);
|
||||
seg2 = (node && node->used > seg1) ? (node->used - seg1) : 0;
|
||||
|
||||
if (p1) {
|
||||
*p1 = (seg1 > 0) ? (uint8_t *)(node->data + node->rpos) : NULL;
|
||||
}
|
||||
if (l1) {
|
||||
*l1 = seg1;
|
||||
}
|
||||
if (p2) {
|
||||
*p2 = (seg2 > 0) ? (uint8_t *)node->data : NULL;
|
||||
}
|
||||
if (l2) {
|
||||
*l2 = seg2;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_write_segments(const chain_buffer_node_t *node,
|
||||
uint8_t **p1, size_t *l1,
|
||||
uint8_t **p2, size_t *l2) {
|
||||
size_t free_total;
|
||||
size_t seg1;
|
||||
size_t seg2;
|
||||
|
||||
if (!node || node->used >= node->cap) {
|
||||
if (p1) *p1 = NULL;
|
||||
if (l1) *l1 = 0;
|
||||
if (p2) *p2 = NULL;
|
||||
if (l2) *l2 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
free_total = node->cap - node->used;
|
||||
seg1 = node_write_seg1_len(node);
|
||||
seg2 = free_total - seg1;
|
||||
|
||||
if (p1) {
|
||||
*p1 = (seg1 > 0) ? (uint8_t *)(node->data + node->wpos) : NULL;
|
||||
}
|
||||
if (l1) {
|
||||
*l1 = seg1;
|
||||
}
|
||||
if (p2) {
|
||||
*p2 = (seg2 > 0) ? (uint8_t *)node->data : NULL;
|
||||
}
|
||||
if (l2) {
|
||||
*l2 = seg2;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_advance_read(chain_buffer_node_t *node, size_t n) {
|
||||
if (!node || n == 0) {
|
||||
return;
|
||||
}
|
||||
node->rpos = (node->rpos + n) % node->cap;
|
||||
node->used -= n;
|
||||
}
|
||||
|
||||
static void node_advance_write(chain_buffer_node_t *node, size_t n) {
|
||||
if (!node || n == 0) {
|
||||
return;
|
||||
}
|
||||
node->wpos = (node->wpos + n) % node->cap;
|
||||
node->used += n;
|
||||
}
|
||||
|
||||
static chain_buffer_node_t *acquire_node(chain_buffer_t *buf, size_t cap) {
|
||||
chain_buffer_node_t *node = NULL;
|
||||
|
||||
if (!buf || cap == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cap == buf->chunk_size && buf->free_list) {
|
||||
node = buf->free_list;
|
||||
buf->free_list = node->next;
|
||||
buf->free_count--;
|
||||
reset_node_state(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
node = alloc_node(cap);
|
||||
return node;
|
||||
}
|
||||
|
||||
static void recycle_node(chain_buffer_t *buf, chain_buffer_node_t *node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!buf || node->cap != buf->chunk_size || buf->free_count >= buf->free_limit) {
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
|
||||
reset_node_state(node);
|
||||
node->next = buf->free_list;
|
||||
buf->free_list = node;
|
||||
buf->free_count++;
|
||||
}
|
||||
|
||||
static int append_new_tail(chain_buffer_t *buf, size_t cap) {
|
||||
chain_buffer_node_t *node;
|
||||
|
||||
node = acquire_node(buf, cap);
|
||||
if (!node) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!buf->tail) {
|
||||
buf->head = node;
|
||||
buf->tail = node;
|
||||
} else {
|
||||
buf->tail->next = node;
|
||||
buf->tail = node;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void list_append_node(chain_buffer_list_t *list, chain_buffer_node_t *node) {
|
||||
if (!list || !node) {
|
||||
return;
|
||||
}
|
||||
node->next = NULL;
|
||||
if (!list->tail) {
|
||||
list->head = node;
|
||||
list->tail = node;
|
||||
} else {
|
||||
list->tail->next = node;
|
||||
list->tail = node;
|
||||
}
|
||||
}
|
||||
|
||||
void chain_buffer_init(chain_buffer_t *buf, size_t chunk_size) {
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
buf->chunk_size = chunk_size ? chunk_size : CHAINBUFFER_DEFAULT_CHUNK;
|
||||
buf->free_limit = CHAINBUFFER_DEFAULT_FREE_LIMIT;
|
||||
}
|
||||
|
||||
void chain_buffer_reset(chain_buffer_t *buf) {
|
||||
chain_buffer_node_t *node;
|
||||
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
chain_buffer_node_t *node = buf->head;
|
||||
node = buf->head;
|
||||
while (node) {
|
||||
chain_buffer_node_t *next = node->next;
|
||||
free(node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
node = buf->free_list;
|
||||
while (node) {
|
||||
chain_buffer_node_t *next = node->next;
|
||||
free(node);
|
||||
@@ -50,11 +247,7 @@ void chain_buffer_reset(chain_buffer_t *buf) {
|
||||
}
|
||||
|
||||
free(buf->linear_cache);
|
||||
buf->linear_cache = NULL;
|
||||
buf->linear_cap = 0;
|
||||
buf->head = NULL;
|
||||
buf->tail = NULL;
|
||||
buf->total_len = 0;
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
}
|
||||
|
||||
size_t chain_buffer_len(const chain_buffer_t *buf) {
|
||||
@@ -62,8 +255,10 @@ size_t chain_buffer_len(const chain_buffer_t *buf) {
|
||||
}
|
||||
|
||||
int chain_buffer_append(chain_buffer_t *buf, const void *data, size_t len) {
|
||||
const uint8_t *src = (const uint8_t *)data;
|
||||
if (!buf || (!src && len > 0)) {
|
||||
const uint8_t *src;
|
||||
size_t remain;
|
||||
|
||||
if (!buf || (!data && len > 0)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
@@ -75,78 +270,97 @@ int chain_buffer_append(chain_buffer_t *buf, const void *data, size_t len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t remain = len;
|
||||
src = (const uint8_t *)data;
|
||||
remain = len;
|
||||
|
||||
while (remain > 0) {
|
||||
chain_buffer_node_t *tail = buf->tail;
|
||||
size_t writable = 0;
|
||||
chain_buffer_node_t *tail;
|
||||
uint8_t *p1;
|
||||
uint8_t *p2;
|
||||
size_t l1;
|
||||
size_t l2;
|
||||
size_t writable;
|
||||
size_t n;
|
||||
size_t c1;
|
||||
|
||||
if (tail && tail->end < tail->cap) {
|
||||
writable = tail->cap - tail->end;
|
||||
}
|
||||
|
||||
if (writable == 0) {
|
||||
if (!buf->tail || buf->tail->used == buf->tail->cap) {
|
||||
size_t cap = remain > buf->chunk_size ? remain : buf->chunk_size;
|
||||
chain_buffer_node_t *node = alloc_node(cap);
|
||||
if (!node) {
|
||||
if (append_new_tail(buf, cap) < 0) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf->tail) {
|
||||
buf->tail->next = node;
|
||||
buf->tail = node;
|
||||
} else {
|
||||
buf->head = node;
|
||||
buf->tail = node;
|
||||
}
|
||||
tail = node;
|
||||
writable = tail->cap;
|
||||
}
|
||||
|
||||
size_t n = remain < writable ? remain : writable;
|
||||
memcpy(tail->data + tail->end, src, n);
|
||||
tail->end += n;
|
||||
tail = buf->tail;
|
||||
node_write_segments(tail, &p1, &l1, &p2, &l2);
|
||||
writable = l1 + l2;
|
||||
if (writable == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n = min_size(remain, writable);
|
||||
c1 = min_size(n, l1);
|
||||
if (c1 > 0) {
|
||||
memcpy(p1, src, c1);
|
||||
}
|
||||
if (n > c1) {
|
||||
memcpy(p2, src + c1, n - c1);
|
||||
}
|
||||
|
||||
node_advance_write(tail, n);
|
||||
buf->total_len += n;
|
||||
src += n;
|
||||
remain -= n;
|
||||
buf->total_len += n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t chain_buffer_drain(chain_buffer_t *buf, size_t len) {
|
||||
size_t remain;
|
||||
size_t drained;
|
||||
|
||||
if (!buf || len == 0 || buf->total_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t remain = len;
|
||||
size_t drained = 0;
|
||||
remain = len;
|
||||
drained = 0;
|
||||
|
||||
while (remain > 0 && buf->head) {
|
||||
chain_buffer_node_t *node = buf->head;
|
||||
size_t avail = node->end - node->start;
|
||||
size_t n = min_size(remain, node->used);
|
||||
|
||||
if (remain < avail) {
|
||||
node->start += remain;
|
||||
buf->total_len -= remain;
|
||||
drained += remain;
|
||||
break;
|
||||
if (n == 0) {
|
||||
buf->head = node->next;
|
||||
if (!buf->head) {
|
||||
buf->tail = NULL;
|
||||
}
|
||||
recycle_node(buf, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
remain -= avail;
|
||||
drained += avail;
|
||||
buf->total_len -= avail;
|
||||
buf->head = node->next;
|
||||
if (!buf->head) {
|
||||
buf->tail = NULL;
|
||||
node_advance_read(node, n);
|
||||
buf->total_len -= n;
|
||||
drained += n;
|
||||
remain -= n;
|
||||
|
||||
if (node->used == 0) {
|
||||
assert(node->refcnt == 0);
|
||||
buf->head = node->next;
|
||||
if (!buf->head) {
|
||||
buf->tail = NULL;
|
||||
}
|
||||
recycle_node(buf, node);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
|
||||
return drained;
|
||||
}
|
||||
|
||||
const uint8_t *chain_buffer_linearize(chain_buffer_t *buf, size_t *out_len) {
|
||||
size_t offset;
|
||||
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -160,7 +374,13 @@ const uint8_t *chain_buffer_linearize(chain_buffer_t *buf, size_t *out_len) {
|
||||
}
|
||||
|
||||
if (buf->head == buf->tail && buf->head) {
|
||||
return buf->head->data + buf->head->start;
|
||||
chain_buffer_node_t *node = buf->head;
|
||||
if (node->used == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (node_read_seg1_len(node) == node->used) {
|
||||
return node->data + node->rpos;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf->linear_cap < buf->total_len) {
|
||||
@@ -172,20 +392,33 @@ const uint8_t *chain_buffer_linearize(chain_buffer_t *buf, size_t *out_len) {
|
||||
buf->linear_cap = buf->total_len;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
offset = 0;
|
||||
for (chain_buffer_node_t *node = buf->head; node; node = node->next) {
|
||||
size_t avail = node->end - node->start;
|
||||
if (avail == 0) {
|
||||
continue;
|
||||
uint8_t *p1;
|
||||
uint8_t *p2;
|
||||
size_t l1;
|
||||
size_t l2;
|
||||
|
||||
node_read_segments(node, &p1, &l1, &p2, &l2);
|
||||
if (l1 > 0) {
|
||||
memcpy(buf->linear_cache + offset, p1, l1);
|
||||
offset += l1;
|
||||
}
|
||||
if (l2 > 0) {
|
||||
memcpy(buf->linear_cache + offset, p2, l2);
|
||||
offset += l2;
|
||||
}
|
||||
memcpy(buf->linear_cache + offset, node->data + node->start, avail);
|
||||
offset += avail;
|
||||
}
|
||||
|
||||
return buf->linear_cache;
|
||||
}
|
||||
|
||||
ssize_t chain_buffer_send_fd(chain_buffer_t *buf, int fd, int flags) {
|
||||
struct iovec iov[CHAINBUFFER_MAX_IOV];
|
||||
struct msghdr msg;
|
||||
size_t iovcnt;
|
||||
ssize_t n;
|
||||
|
||||
if (!buf) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
@@ -194,34 +427,277 @@ ssize_t chain_buffer_send_fd(chain_buffer_t *buf, int fd, int flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iovec iov[CHAINBUFFER_MAX_IOV];
|
||||
size_t iovcnt = 0;
|
||||
|
||||
iovcnt = 0;
|
||||
for (chain_buffer_node_t *node = buf->head;
|
||||
node && iovcnt < CHAINBUFFER_MAX_IOV;
|
||||
node = node->next) {
|
||||
size_t avail = node->end - node->start;
|
||||
if (avail == 0) {
|
||||
continue;
|
||||
uint8_t *p1;
|
||||
uint8_t *p2;
|
||||
size_t l1;
|
||||
size_t l2;
|
||||
|
||||
node_read_segments(node, &p1, &l1, &p2, &l2);
|
||||
if (l1 > 0) {
|
||||
iov[iovcnt].iov_base = p1;
|
||||
iov[iovcnt].iov_len = l1;
|
||||
iovcnt++;
|
||||
if (iovcnt >= CHAINBUFFER_MAX_IOV) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (l2 > 0) {
|
||||
iov[iovcnt].iov_base = p2;
|
||||
iov[iovcnt].iov_len = l2;
|
||||
iovcnt++;
|
||||
}
|
||||
iov[iovcnt].iov_base = (void *)(node->data + node->start);
|
||||
iov[iovcnt].iov_len = avail;
|
||||
iovcnt++;
|
||||
}
|
||||
|
||||
if (iovcnt == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = iovcnt;
|
||||
|
||||
ssize_t n = sendmsg(fd, &msg, flags);
|
||||
n = sendmsg(fd, &msg, flags);
|
||||
if (n > 0) {
|
||||
chain_buffer_drain(buf, (size_t)n);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int chain_buffer_prepare_recv_iov(chain_buffer_t *buf, struct iovec *iov, int max_iov) {
|
||||
chain_buffer_node_t *tail;
|
||||
uint8_t *p1;
|
||||
uint8_t *p2;
|
||||
size_t l1;
|
||||
size_t l2;
|
||||
int iovcnt;
|
||||
|
||||
if (!buf || !iov || max_iov <= 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!buf->tail || buf->tail->used == buf->tail->cap) {
|
||||
if (append_new_tail(buf, buf->chunk_size) < 0) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
tail = buf->tail;
|
||||
node_write_segments(tail, &p1, &l1, &p2, &l2);
|
||||
|
||||
iovcnt = 0;
|
||||
if (l1 > 0) {
|
||||
iov[iovcnt].iov_base = p1;
|
||||
iov[iovcnt].iov_len = l1;
|
||||
iovcnt++;
|
||||
}
|
||||
if (l2 > 0 && iovcnt < max_iov) {
|
||||
iov[iovcnt].iov_base = p2;
|
||||
iov[iovcnt].iov_len = l2;
|
||||
iovcnt++;
|
||||
}
|
||||
|
||||
return iovcnt;
|
||||
}
|
||||
|
||||
size_t chain_buffer_commit_recv(chain_buffer_t *buf, size_t len) {
|
||||
chain_buffer_node_t *tail;
|
||||
size_t free_total;
|
||||
|
||||
if (!buf || len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!buf->tail || buf->tail->used == buf->tail->cap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tail = buf->tail;
|
||||
free_total = tail->cap - tail->used;
|
||||
if (len > free_total) {
|
||||
len = free_total;
|
||||
}
|
||||
|
||||
node_advance_write(tail, len);
|
||||
buf->total_len += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
void chain_buffer_list_init(chain_buffer_list_t *list) {
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
memset(list, 0, sizeof(*list));
|
||||
}
|
||||
|
||||
size_t chain_buffer_list_len(const chain_buffer_list_t *list) {
|
||||
return list ? list->total_len : 0;
|
||||
}
|
||||
|
||||
int chain_buffer_list_iov(const chain_buffer_list_t *list, struct iovec *iov, int max_iov) {
|
||||
int iovcnt = 0;
|
||||
|
||||
if (!list || !iov || max_iov <= 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (chain_buffer_node_t *node = list->head; node && iovcnt < max_iov; node = node->next) {
|
||||
uint8_t *p1;
|
||||
uint8_t *p2;
|
||||
size_t l1;
|
||||
size_t l2;
|
||||
|
||||
node_read_segments(node, &p1, &l1, &p2, &l2);
|
||||
if (l1 > 0) {
|
||||
iov[iovcnt].iov_base = p1;
|
||||
iov[iovcnt].iov_len = l1;
|
||||
iovcnt++;
|
||||
if (iovcnt >= max_iov) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (l2 > 0) {
|
||||
iov[iovcnt].iov_base = p2;
|
||||
iov[iovcnt].iov_len = l2;
|
||||
iovcnt++;
|
||||
}
|
||||
}
|
||||
|
||||
return iovcnt;
|
||||
}
|
||||
|
||||
static int copy_prefix_from_node(chain_buffer_node_t *node, size_t len, uint8_t *dst) {
|
||||
uint8_t *p1;
|
||||
uint8_t *p2;
|
||||
size_t l1;
|
||||
size_t l2;
|
||||
size_t c1;
|
||||
|
||||
if (!node || !dst || len == 0 || len > node->used) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
node_read_segments(node, &p1, &l1, &p2, &l2);
|
||||
c1 = min_size(len, l1);
|
||||
if (c1 > 0) {
|
||||
memcpy(dst, p1, c1);
|
||||
}
|
||||
if (len > c1) {
|
||||
memcpy(dst + c1, p2, len - c1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chain_buffer_detach_prefix(chain_buffer_t *buf, size_t len, chain_buffer_list_t *out) {
|
||||
size_t remain;
|
||||
|
||||
if (!buf || !out) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
chain_buffer_list_init(out);
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (len > buf->total_len) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
remain = len;
|
||||
while (remain > 0 && buf->head) {
|
||||
chain_buffer_node_t *node = buf->head;
|
||||
|
||||
if (remain >= node->used) {
|
||||
size_t take = node->used;
|
||||
|
||||
buf->head = node->next;
|
||||
if (!buf->head) {
|
||||
buf->tail = NULL;
|
||||
}
|
||||
node->next = NULL;
|
||||
node->refcnt = 1;
|
||||
list_append_node(out, node);
|
||||
out->total_len += take;
|
||||
buf->total_len -= take;
|
||||
remain -= take;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
chain_buffer_node_t *part = acquire_node(buf, remain);
|
||||
if (!part) {
|
||||
chain_buffer_list_release(buf, out);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (copy_prefix_from_node(node, remain, part->data) < 0) {
|
||||
recycle_node(buf, part);
|
||||
chain_buffer_list_release(buf, out);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
part->used = remain;
|
||||
part->wpos = remain % part->cap;
|
||||
part->rpos = 0;
|
||||
part->refcnt = 1;
|
||||
part->next = NULL;
|
||||
list_append_node(out, part);
|
||||
out->total_len += remain;
|
||||
|
||||
node_advance_read(node, remain);
|
||||
buf->total_len -= remain;
|
||||
remain = 0;
|
||||
|
||||
if (node->used == 0) {
|
||||
buf->head = node->next;
|
||||
if (!buf->head) {
|
||||
buf->tail = NULL;
|
||||
}
|
||||
recycle_node(buf, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void chain_buffer_list_release(chain_buffer_t *owner, chain_buffer_list_t *list) {
|
||||
chain_buffer_node_t *node;
|
||||
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
node = list->head;
|
||||
while (node) {
|
||||
chain_buffer_node_t *next = node->next;
|
||||
|
||||
if (node->refcnt > 0) {
|
||||
node->refcnt--;
|
||||
}
|
||||
|
||||
if (node->refcnt == 0) {
|
||||
if (owner) {
|
||||
recycle_node(owner, node);
|
||||
} else {
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
|
||||
chain_buffer_list_init(list);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user