| 572 | } |
| 573 | |
| 574 | void setDeferredReply(client *c, void *node, const char *s, size_t length) { |
| 575 | listNode *ln = (listNode*)node; |
| 576 | clientReplyBlock *next, *prev; |
| 577 | |
| 578 | /* Abort when *node is NULL: when the client should not accept writes |
| 579 | * we return NULL in addReplyDeferredLen() */ |
| 580 | if (node == NULL) return; |
| 581 | serverAssert(!listNodeValue(ln)); |
| 582 | |
| 583 | /* Normally we fill this dummy NULL node, added by addReplyDeferredLen(), |
| 584 | * with a new buffer structure containing the protocol needed to specify |
| 585 | * the length of the array following. However sometimes there might be room |
| 586 | * in the previous/next node so we can instead remove this NULL node, and |
| 587 | * suffix/prefix our data in the node immediately before/after it, in order |
| 588 | * to save a write(2) syscall later. Conditions needed to do it: |
| 589 | * |
| 590 | * - The prev node is non-NULL and has space in it or |
| 591 | * - The next node is non-NULL, |
| 592 | * - It has enough room already allocated |
| 593 | * - And not too large (avoid large memmove) */ |
| 594 | if (ln->prev != NULL && (prev = listNodeValue(ln->prev)) && |
| 595 | prev->size - prev->used > 0) |
| 596 | { |
| 597 | size_t len_to_copy = prev->size - prev->used; |
| 598 | if (len_to_copy > length) |
| 599 | len_to_copy = length; |
| 600 | memcpy(prev->buf + prev->used, s, len_to_copy); |
| 601 | prev->used += len_to_copy; |
| 602 | length -= len_to_copy; |
| 603 | if (length == 0) { |
| 604 | listDelNode(c->reply, ln); |
| 605 | return; |
| 606 | } |
| 607 | s += len_to_copy; |
| 608 | } |
| 609 | |
| 610 | if (ln->next != NULL && (next = listNodeValue(ln->next)) && |
| 611 | next->size - next->used >= length && |
| 612 | next->used < PROTO_REPLY_CHUNK_BYTES * 4) |
| 613 | { |
| 614 | memmove(next->buf + length, next->buf, next->used); |
| 615 | memcpy(next->buf, s, length); |
| 616 | next->used += length; |
| 617 | listDelNode(c->reply,ln); |
| 618 | } else { |
| 619 | /* Create a new node */ |
| 620 | clientReplyBlock *buf = zmalloc(length + sizeof(clientReplyBlock)); |
| 621 | /* Take over the allocation's internal fragmentation */ |
| 622 | buf->size = zmalloc_usable_size(buf) - sizeof(clientReplyBlock); |
| 623 | buf->used = length; |
| 624 | memcpy(buf->buf, s, length); |
| 625 | listNodeValue(ln) = buf; |
| 626 | c->reply_bytes += buf->size; |
| 627 | |
| 628 | closeClientOnOutputBufferLimitReached(c, 1); |
| 629 | } |
| 630 | } |
| 631 |
no test coverage detected