| 710 | } |
| 711 | |
| 712 | void setDeferredReply(client *c, void *node, const char *s, size_t length) { |
| 713 | listNode *ln = (listNode*)node; |
| 714 | clientReplyBlock *next, *prev; |
| 715 | |
| 716 | /* Abort when *node is NULL: when the client should not accept writes |
| 717 | * we return NULL in addReplyDeferredLen() */ |
| 718 | if (node == NULL) return; |
| 719 | serverAssert(!listNodeValue(ln)); |
| 720 | |
| 721 | /* Normally we fill this dummy NULL node, added by addReplyDeferredLen(), |
| 722 | * with a new buffer structure containing the protocol needed to specify |
| 723 | * the length of the array following. However sometimes there might be room |
| 724 | * in the previous/next node so we can instead remove this NULL node, and |
| 725 | * suffix/prefix our data in the node immediately before/after it, in order |
| 726 | * to save a write(2) syscall later. Conditions needed to do it: |
| 727 | * |
| 728 | * - The prev node is non-NULL and has space in it or |
| 729 | * - The next node is non-NULL, |
| 730 | * - It has enough room already allocated |
| 731 | * - And not too large (avoid large memmove) */ |
| 732 | if (ln->prev != NULL && (prev = (clientReplyBlock*)listNodeValue(ln->prev)) && |
| 733 | prev->size - prev->used > 0) |
| 734 | { |
| 735 | size_t len_to_copy = prev->size - prev->used; |
| 736 | if (len_to_copy > length) |
| 737 | len_to_copy = length; |
| 738 | memcpy(prev->buf() + prev->used, s, len_to_copy); |
| 739 | prev->used += len_to_copy; |
| 740 | length -= len_to_copy; |
| 741 | if (length == 0) { |
| 742 | listDelNode(c->reply, ln); |
| 743 | return; |
| 744 | } |
| 745 | s += len_to_copy; |
| 746 | } |
| 747 | |
| 748 | if (ln->next != NULL && (next = (clientReplyBlock*)listNodeValue(ln->next)) && |
| 749 | next->size - next->used >= length && |
| 750 | next->used < PROTO_REPLY_CHUNK_BYTES * 4) |
| 751 | { |
| 752 | memmove(next->buf() + length, next->buf(), next->used); |
| 753 | memcpy(next->buf(), s, length); |
| 754 | next->used += length; |
| 755 | listDelNode(c->reply,ln); |
| 756 | } else { |
| 757 | /* Create a new node */ |
| 758 | clientReplyBlock *buf = (clientReplyBlock*)zmalloc(length + sizeof(clientReplyBlock)); |
| 759 | /* Take over the allocation's internal fragmentation */ |
| 760 | buf->size = zmalloc_usable_size(buf) - sizeof(clientReplyBlock); |
| 761 | buf->used = length; |
| 762 | memcpy(buf->buf(), s, length); |
| 763 | listNodeValue(ln) = buf; |
| 764 | c->reply_bytes += buf->size; |
| 765 | |
| 766 | closeClientOnOutputBufferLimitReached(c, 1); |
| 767 | } |
| 768 | } |
| 769 |
no test coverage detected