mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-10-22 05:01:07 +02:00
With this commit, we now separate the channel from the buffer. This will allow us to replace buffers on the fly without touching the channel. Since nobody is supposed to keep a reference to a buffer anymore, doing so is not a problem and will also permit some copy-less data manipulation. Interestingly, these changes have shown a 2% performance increase on some workloads, probably due to a better cache placement of data.
227 lines
5.8 KiB
C
227 lines
5.8 KiB
C
/*
|
|
* Buffer management functions.
|
|
*
|
|
* Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <common/config.h>
|
|
#include <common/buffer.h>
|
|
#include <common/memory.h>
|
|
|
|
#include <types/global.h>
|
|
|
|
struct pool_head *pool2_buffer;
|
|
|
|
|
|
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
|
int init_buffer()
|
|
{
|
|
pool2_buffer = create_pool("buffer", sizeof (struct buffer) + global.tune.bufsize, MEM_F_SHARED);
|
|
return pool2_buffer != NULL;
|
|
}
|
|
|
|
/* This function writes the string <str> at position <pos> which must be in
|
|
* buffer <b>, and moves <end> just after the end of <str>. <b>'s parameters
|
|
* <l> and <r> are updated to be valid after the shift. The shift value
|
|
* (positive or negative) is returned. If there's no space left, the move is
|
|
* not done. The function does not adjust ->o because it does not make sense to
|
|
* use it on data scheduled to be sent. For the same reason, it does not make
|
|
* sense to call this function on unparsed data, so <orig> is not updated. The
|
|
* string length is taken from parameter <len>. If <len> is null, the <str>
|
|
* pointer is allowed to be null.
|
|
*/
|
|
int buffer_replace2(struct buffer *b, char *pos, char *end, const char *str, int len)
|
|
{
|
|
int delta;
|
|
|
|
delta = len - (end - pos);
|
|
|
|
if (bi_end(b) + delta >= b->data + b->size)
|
|
return 0; /* no space left */
|
|
|
|
if (buffer_not_empty(b) &&
|
|
bi_end(b) + delta > bo_ptr(b) &&
|
|
bo_ptr(b) >= bi_end(b))
|
|
return 0; /* no space left before wrapping data */
|
|
|
|
/* first, protect the end of the buffer */
|
|
memmove(end + delta, end, bi_end(b) - end);
|
|
|
|
/* now, copy str over pos */
|
|
if (len)
|
|
memcpy(pos, str, len);
|
|
|
|
b->i += delta;
|
|
|
|
if (buffer_len(b) == 0)
|
|
b->p = b->data;
|
|
|
|
return delta;
|
|
}
|
|
|
|
/*
|
|
* Inserts <str> followed by "\r\n" at position <pos> in buffer <b>. The <len>
|
|
* argument informs about the length of string <str> so that we don't have to
|
|
* measure it. It does not include the "\r\n". If <str> is NULL, then the buffer
|
|
* is only opened for len+2 bytes but nothing is copied in. It may be useful in
|
|
* some circumstances. The send limit is *not* adjusted. Same comments as above
|
|
* for the valid use cases.
|
|
*
|
|
* The number of bytes added is returned on success. 0 is returned on failure.
|
|
*/
|
|
int buffer_insert_line2(struct buffer *b, char *pos, const char *str, int len)
|
|
{
|
|
int delta;
|
|
|
|
delta = len + 2;
|
|
|
|
if (bi_end(b) + delta >= b->data + b->size)
|
|
return 0; /* no space left */
|
|
|
|
/* first, protect the end of the buffer */
|
|
memmove(pos + delta, pos, bi_end(b) - pos);
|
|
|
|
/* now, copy str over pos */
|
|
if (len && str) {
|
|
memcpy(pos, str, len);
|
|
pos[len] = '\r';
|
|
pos[len + 1] = '\n';
|
|
}
|
|
|
|
b->i += delta;
|
|
return delta;
|
|
}
|
|
|
|
/* This function realigns input data in a possibly wrapping buffer so that it
|
|
* becomes contiguous and starts at the beginning of the buffer area. The
|
|
* function may only be used when the buffer's output is empty.
|
|
*/
|
|
void buffer_slow_realign(struct buffer *buf)
|
|
{
|
|
/* two possible cases :
|
|
* - the buffer is in one contiguous block, we move it in-place
|
|
* - the buffer is in two blocks, we move it via the swap_buffer
|
|
*/
|
|
if (buf->i) {
|
|
int block1 = buf->i;
|
|
int block2 = 0;
|
|
if (buf->p + buf->i > buf->data + buf->size) {
|
|
/* non-contiguous block */
|
|
block1 = buf->data + buf->size - buf->p;
|
|
block2 = buf->p + buf->i - (buf->data + buf->size);
|
|
}
|
|
if (block2)
|
|
memcpy(swap_buffer, buf->data, block2);
|
|
memmove(buf->data, buf->p, block1);
|
|
if (block2)
|
|
memcpy(buf->data + block1, swap_buffer, block2);
|
|
}
|
|
|
|
buf->p = buf->data;
|
|
}
|
|
|
|
|
|
/* Realigns a possibly non-contiguous buffer by bouncing bytes from source to
|
|
* destination. It does not use any intermediate buffer and does the move in
|
|
* place, though it will be slower than a simple memmove() on contiguous data,
|
|
* so it's desirable to use it only on non-contiguous buffers. No pointers are
|
|
* changed, the caller is responsible for that.
|
|
*/
|
|
void buffer_bounce_realign(struct buffer *buf)
|
|
{
|
|
int advance, to_move;
|
|
char *from, *to;
|
|
|
|
from = bo_ptr(buf);
|
|
advance = buf->data + buf->size - from;
|
|
if (!advance)
|
|
return;
|
|
|
|
to_move = buffer_len(buf);
|
|
while (to_move) {
|
|
char last, save;
|
|
|
|
last = *from;
|
|
to = from + advance;
|
|
if (to >= buf->data + buf->size)
|
|
to -= buf->size;
|
|
|
|
while (1) {
|
|
save = *to;
|
|
*to = last;
|
|
last = save;
|
|
to_move--;
|
|
if (!to_move)
|
|
break;
|
|
|
|
/* check if we went back home after rotating a number of bytes */
|
|
if (to == from)
|
|
break;
|
|
|
|
/* if we ended up in the empty area, let's walk to next place. The
|
|
* empty area is either between buf->r and from or before from or
|
|
* after buf->r.
|
|
*/
|
|
if (from > bi_end(buf)) {
|
|
if (to >= bi_end(buf) && to < from)
|
|
break;
|
|
} else if (from < bi_end(buf)) {
|
|
if (to < from || to >= bi_end(buf))
|
|
break;
|
|
}
|
|
|
|
/* we have overwritten a byte of the original set, let's move it */
|
|
to += advance;
|
|
if (to >= buf->data + buf->size)
|
|
to -= buf->size;
|
|
}
|
|
|
|
from++;
|
|
if (from >= buf->data + buf->size)
|
|
from -= buf->size;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Dumps part or all of a buffer.
|
|
*/
|
|
void buffer_dump(FILE *o, struct buffer *b, int from, int to)
|
|
{
|
|
fprintf(o, "Dumping buffer %p\n", b);
|
|
fprintf(o, " data=%p o=%d i=%d p=%p\n",
|
|
b->data, b->o, b->i, b->p);
|
|
|
|
if (!to || to > buffer_len(b))
|
|
to = buffer_len(b);
|
|
|
|
fprintf(o, "Dumping contents from byte %d to byte %d\n", from, to);
|
|
for (; from < to; from++) {
|
|
if ((from & 15) == 0)
|
|
fprintf(o, " %04x: ", from);
|
|
fprintf(o, "%02x ", b->data[from]);
|
|
if ((from & 15) == 7)
|
|
fprintf(o, "- ");
|
|
else if (((from & 15) == 15) && (from != to-1))
|
|
fprintf(o, "\n");
|
|
}
|
|
fprintf(o, "\n--\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-indent-level: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|