mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-22 14:21:25 +02:00
At the moment, the struct is still embedded into the struct channel, but all the functions have been updated to use struct buffer only when possible, otherwise struct channel. Some functions would likely need to be splitted between a buffer-layer primitive and a channel-layer function. Later the buffer should become a pointer in the struct buffer, but doing so requires a few changes to the buffer allocation calls.
764 lines
22 KiB
C
764 lines
22 KiB
C
/*
|
|
* include/proto/buffers.h
|
|
* Buffer management definitions, macros and inline functions.
|
|
*
|
|
* Copyright (C) 2000-2012 Willy Tarreau - w@1wt.eu
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation, version 2.1
|
|
* exclusively.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef _PROTO_BUFFERS_H
|
|
#define _PROTO_BUFFERS_H
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <common/config.h>
|
|
#include <common/memory.h>
|
|
#include <common/ticks.h>
|
|
#include <common/time.h>
|
|
|
|
#include <types/buffers.h>
|
|
#include <types/global.h>
|
|
|
|
extern struct pool_head *pool2_buffer;
|
|
|
|
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
|
int init_buffer();
|
|
|
|
/* SI-to-buffer functions : buffer_{get,put}_{char,block,string,chunk} */
|
|
int bo_inject(struct channel *buf, const char *msg, int len);
|
|
int bi_putblk(struct channel *buf, const char *str, int len);
|
|
int bi_putchr(struct channel *buf, char c);
|
|
int bo_getline(struct channel *buf, char *str, int len);
|
|
int bo_getblk(struct channel *buf, char *blk, int len, int offset);
|
|
int buffer_replace2(struct channel *b, char *pos, char *end, const char *str, int len);
|
|
int buffer_insert_line2(struct channel *b, char *pos, const char *str, int len);
|
|
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
|
void buffer_slow_realign(struct buffer *buf);
|
|
void buffer_bounce_realign(struct buffer *buf);
|
|
unsigned long long buffer_forward(struct channel *buf, unsigned long long bytes);
|
|
|
|
/* Initialize all fields in the buffer. The BF_OUT_EMPTY flags is set. */
|
|
static inline void buffer_init(struct channel *buf)
|
|
{
|
|
buf->buf.o = 0;
|
|
buf->buf.i = 0;
|
|
buf->buf.p = buf->buf.data;
|
|
buf->to_forward = 0;
|
|
buf->total = 0;
|
|
buf->pipe = NULL;
|
|
buf->analysers = 0;
|
|
buf->cons = NULL;
|
|
buf->flags = BF_OUT_EMPTY;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/* These functions are used to compute various buffer area sizes */
|
|
/*****************************************************************/
|
|
|
|
/* Returns an absolute pointer for a position relative to the current buffer's
|
|
* pointer. It is written so that it is optimal when <ofs> is a const. It is
|
|
* written as a macro instead of an inline function so that the compiler knows
|
|
* when it can optimize out the sign test on <ofs> when passed an unsigned int.
|
|
*/
|
|
#define b_ptr(b, ofs) \
|
|
({ \
|
|
char *__ret = (b)->p + (ofs); \
|
|
if ((ofs) > 0 && __ret >= (b)->data + (b)->size) \
|
|
__ret -= (b)->size; \
|
|
else if ((ofs) < 0 && __ret < (b)->data) \
|
|
__ret += (b)->size; \
|
|
__ret; \
|
|
})
|
|
|
|
/* Returns the start of the input data in a buffer */
|
|
static inline char *bi_ptr(const struct buffer *b)
|
|
{
|
|
return b->p;
|
|
}
|
|
|
|
/* Returns the end of the input data in a buffer (pointer to next
|
|
* insertion point).
|
|
*/
|
|
static inline char *bi_end(const struct buffer *b)
|
|
{
|
|
char *ret = b->p + b->i;
|
|
|
|
if (ret >= b->data + b->size)
|
|
ret -= b->size;
|
|
return ret;
|
|
}
|
|
|
|
/* Returns the amount of input data that can contiguously be read at once */
|
|
static inline int bi_contig_data(const struct buffer *b)
|
|
{
|
|
int data = b->data + b->size - b->p;
|
|
|
|
if (data > b->i)
|
|
data = b->i;
|
|
return data;
|
|
}
|
|
|
|
/* Returns the start of the output data in a buffer */
|
|
static inline char *bo_ptr(const struct buffer *b)
|
|
{
|
|
char *ret = b->p - b->o;
|
|
|
|
if (ret < b->data)
|
|
ret += b->size;
|
|
return ret;
|
|
}
|
|
|
|
/* Returns the end of the output data in a buffer */
|
|
static inline char *bo_end(const struct buffer *b)
|
|
{
|
|
return b->p;
|
|
}
|
|
|
|
/* Returns the amount of output data that can contiguously be read at once */
|
|
static inline int bo_contig_data(const struct buffer *b)
|
|
{
|
|
char *beg = b->p - b->o;
|
|
|
|
if (beg < b->data)
|
|
return b->data - beg;
|
|
return b->o;
|
|
}
|
|
|
|
/* Return the buffer's length in bytes by summing the input and the output */
|
|
static inline int buffer_len(const struct buffer *buf)
|
|
{
|
|
return buf->i + buf->o;
|
|
}
|
|
|
|
/* Return non-zero only if the buffer is not empty */
|
|
static inline int buffer_not_empty(const struct buffer *buf)
|
|
{
|
|
return buf->i | buf->o;
|
|
}
|
|
|
|
/* Return non-zero only if the buffer is empty */
|
|
static inline int buffer_empty(const struct buffer *buf)
|
|
{
|
|
return !buffer_not_empty(buf);
|
|
}
|
|
|
|
/* Normalizes a pointer after a subtract */
|
|
static inline char *buffer_wrap_sub(const struct buffer *buf, char *ptr)
|
|
{
|
|
if (ptr < buf->data)
|
|
ptr += buf->size;
|
|
return ptr;
|
|
}
|
|
|
|
/* Normalizes a pointer after an addition */
|
|
static inline char *buffer_wrap_add(const struct buffer *buf, char *ptr)
|
|
{
|
|
if (ptr - buf->size >= buf->data)
|
|
ptr -= buf->size;
|
|
return ptr;
|
|
}
|
|
|
|
/* Return the number of reserved bytes in the buffer, which ensures that once
|
|
* all pending data are forwarded, the buffer still has global.tune.maxrewrite
|
|
* bytes free. The result is between 0 and global.maxrewrite, which is itself
|
|
* smaller than any buf->size.
|
|
*/
|
|
static inline int buffer_reserved(const struct channel *buf)
|
|
{
|
|
int ret = global.tune.maxrewrite - buf->to_forward - buf->buf.o;
|
|
|
|
if (buf->to_forward == BUF_INFINITE_FORWARD)
|
|
return 0;
|
|
if (ret <= 0)
|
|
return 0;
|
|
return ret;
|
|
}
|
|
|
|
/* Return the max number of bytes the buffer can contain so that once all the
|
|
* pending bytes are forwarded, the buffer still has global.tune.maxrewrite
|
|
* bytes free. The result sits between buf->size - maxrewrite and buf->size.
|
|
*/
|
|
static inline int buffer_max_len(const struct channel *buf)
|
|
{
|
|
return buf->buf.size - buffer_reserved(buf);
|
|
}
|
|
|
|
/* Returns non-zero if the buffer input is considered full. The reserved space
|
|
* is taken into account if ->to_forward indicates that an end of transfer is
|
|
* close to happen. The test is optimized to avoid as many operations as
|
|
* possible for the fast case and to be used as an "if" condition.
|
|
*/
|
|
static inline int bi_full(const struct channel *b)
|
|
{
|
|
int rem = b->buf.size;
|
|
|
|
rem -= b->buf.o;
|
|
rem -= b->buf.i;
|
|
if (!rem)
|
|
return 1; /* buffer already full */
|
|
|
|
if (b->to_forward >= b->buf.size ||
|
|
(BUF_INFINITE_FORWARD < MAX_RANGE(typeof(b->buf.size)) && // just there to ensure gcc
|
|
b->to_forward == BUF_INFINITE_FORWARD)) // avoids the useless second
|
|
return 0; // test whenever possible
|
|
|
|
rem -= global.tune.maxrewrite;
|
|
rem += b->buf.o;
|
|
rem += b->to_forward;
|
|
return rem <= 0;
|
|
}
|
|
|
|
/* Returns the amount of space available at the input of the buffer, taking the
|
|
* reserved space into account if ->to_forward indicates that an end of transfer
|
|
* is close to happen. The test is optimized to avoid as many operations as
|
|
* possible for the fast case.
|
|
*/
|
|
static inline int bi_avail(const struct channel *b)
|
|
{
|
|
int rem = b->buf.size;
|
|
int rem2;
|
|
|
|
rem -= b->buf.o;
|
|
rem -= b->buf.i;
|
|
if (!rem)
|
|
return rem; /* buffer already full */
|
|
|
|
if (b->to_forward >= b->buf.size ||
|
|
(BUF_INFINITE_FORWARD < MAX_RANGE(typeof(b->buf.size)) && // just there to ensure gcc
|
|
b->to_forward == BUF_INFINITE_FORWARD)) // avoids the useless second
|
|
return rem; // test whenever possible
|
|
|
|
rem2 = rem - global.tune.maxrewrite;
|
|
rem2 += b->buf.o;
|
|
rem2 += b->to_forward;
|
|
|
|
if (rem > rem2)
|
|
rem = rem2;
|
|
if (rem > 0)
|
|
return rem;
|
|
return 0;
|
|
}
|
|
|
|
/* Return the maximum amount of bytes that can be written into the buffer,
|
|
* including reserved space which may be overwritten.
|
|
*/
|
|
static inline int buffer_total_space(const struct buffer *buf)
|
|
{
|
|
return buf->size - buffer_len(buf);
|
|
}
|
|
|
|
/* Returns the number of contiguous bytes between <start> and <start>+<count>,
|
|
* and enforces a limit on buf->data + buf->size. <start> must be within the
|
|
* buffer.
|
|
*/
|
|
static inline int buffer_contig_area(const struct buffer *buf, const char *start, int count)
|
|
{
|
|
if (count > buf->data - start + buf->size)
|
|
count = buf->data - start + buf->size;
|
|
return count;
|
|
}
|
|
|
|
/* Return the amount of bytes that can be written into the buffer at once,
|
|
* including reserved space which may be overwritten.
|
|
*/
|
|
static inline int buffer_contig_space(const struct buffer *buf)
|
|
{
|
|
const char *left, *right;
|
|
|
|
if (buf->data + buf->o <= buf->p)
|
|
right = buf->data + buf->size;
|
|
else
|
|
right = buf->p + buf->size - buf->o;
|
|
|
|
left = buffer_wrap_add(buf, buf->p + buf->i);
|
|
return right - left;
|
|
}
|
|
|
|
/* Advances the buffer by <adv> bytes, which means that the buffer
|
|
* pointer advances, and that as many bytes from in are transferred
|
|
* to out. The caller is responsible for ensuring that adv is always
|
|
* smaller than or equal to b->i. The BF_OUT_EMPTY flag is updated.
|
|
*/
|
|
static inline void b_adv(struct channel *b, unsigned int adv)
|
|
{
|
|
b->buf.i -= adv;
|
|
b->buf.o += adv;
|
|
if (b->buf.o)
|
|
b->flags &= ~BF_OUT_EMPTY;
|
|
b->buf.p = b_ptr(&b->buf, adv);
|
|
}
|
|
|
|
/* Rewinds the buffer by <adv> bytes, which means that the buffer pointer goes
|
|
* backwards, and that as many bytes from out are moved to in. The caller is
|
|
* responsible for ensuring that adv is always smaller than or equal to b->o.
|
|
*/
|
|
static inline void b_rew(struct channel *b, unsigned int adv)
|
|
{
|
|
b->buf.i += adv;
|
|
b->buf.o -= adv;
|
|
if (!b->buf.o && !b->pipe)
|
|
b->flags |= BF_OUT_EMPTY;
|
|
b->buf.p = b_ptr(&b->buf, (int)-adv);
|
|
}
|
|
|
|
/* Return the amount of bytes that can be written into the buffer at once,
|
|
* excluding the amount of reserved space passed in <res>, which is
|
|
* preserved.
|
|
*/
|
|
static inline int buffer_contig_space_with_res(const struct buffer *buf, int res)
|
|
{
|
|
/* Proceed differently if the buffer is full, partially used or empty.
|
|
* The hard situation is when it's partially used and either data or
|
|
* reserved space wraps at the end.
|
|
*/
|
|
int spare = buf->size - res;
|
|
|
|
if (buffer_len(buf) >= spare)
|
|
spare = 0;
|
|
else if (buffer_len(buf)) {
|
|
spare = buffer_contig_space(buf) - res;
|
|
if (spare < 0)
|
|
spare = 0;
|
|
}
|
|
return spare;
|
|
}
|
|
|
|
|
|
/* Return the amount of bytes that can be written into the buffer at once,
|
|
* excluding reserved space, which is preserved.
|
|
*/
|
|
static inline int buffer_contig_space_res(const struct channel *chn)
|
|
{
|
|
return buffer_contig_space_with_res(&chn->buf, buffer_reserved(chn));
|
|
}
|
|
|
|
/* Normalizes a pointer which is supposed to be relative to the beginning of a
|
|
* buffer, so that wrapping is correctly handled. The intent is to use this
|
|
* when increasing a pointer. Note that the wrapping test is only performed
|
|
* once, so the original pointer must be between ->data-size and ->data+2*size-1,
|
|
* otherwise an invalid pointer might be returned.
|
|
*/
|
|
static inline const char *buffer_pointer(const struct buffer *buf, const char *ptr)
|
|
{
|
|
if (ptr < buf->data)
|
|
ptr += buf->size;
|
|
else if (ptr - buf->size >= buf->data)
|
|
ptr -= buf->size;
|
|
return ptr;
|
|
}
|
|
|
|
/* Returns the distance between two pointers, taking into account the ability
|
|
* to wrap around the buffer's end.
|
|
*/
|
|
static inline int buffer_count(const struct buffer *buf, const char *from, const char *to)
|
|
{
|
|
int count = to - from;
|
|
if (count < 0)
|
|
count += buf->size;
|
|
return count;
|
|
}
|
|
|
|
/* returns the amount of pending bytes in the buffer. It is the amount of bytes
|
|
* that is not scheduled to be sent.
|
|
*/
|
|
static inline int buffer_pending(const struct buffer *buf)
|
|
{
|
|
return buf->i;
|
|
}
|
|
|
|
/* Returns the size of the working area which the caller knows ends at <end>.
|
|
* If <end> equals buf->r (modulo size), then it means that the free area which
|
|
* follows is part of the working area. Otherwise, the working area stops at
|
|
* <end>. It always starts at buf->p. The work area includes the
|
|
* reserved area.
|
|
*/
|
|
static inline int buffer_work_area(const struct buffer *buf, const char *end)
|
|
{
|
|
end = buffer_pointer(buf, end);
|
|
if (end == buffer_wrap_add(buf, buf->p + buf->i))
|
|
/* pointer exactly at end, lets push forwards */
|
|
end = buffer_wrap_sub(buf, buf->p - buf->o);
|
|
return buffer_count(buf, buf->p, end);
|
|
}
|
|
|
|
/* Return 1 if the buffer has less than 1/4 of its capacity free, otherwise 0 */
|
|
static inline int buffer_almost_full(const struct buffer *buf)
|
|
{
|
|
if (buffer_total_space(buf) < buf->size / 4)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Returns true if the buffer's input is already closed */
|
|
static inline int buffer_input_closed(struct channel *buf)
|
|
{
|
|
return ((buf->flags & BF_SHUTR) != 0);
|
|
}
|
|
|
|
/* Returns true if the buffer's output is already closed */
|
|
static inline int buffer_output_closed(struct channel *buf)
|
|
{
|
|
return ((buf->flags & BF_SHUTW) != 0);
|
|
}
|
|
|
|
/* Check buffer timeouts, and set the corresponding flags. The
|
|
* likely/unlikely have been optimized for fastest normal path.
|
|
* The read/write timeouts are not set if there was activity on the buffer.
|
|
* That way, we don't have to update the timeout on every I/O. Note that the
|
|
* analyser timeout is always checked.
|
|
*/
|
|
static inline void buffer_check_timeouts(struct channel *b)
|
|
{
|
|
if (likely(!(b->flags & (BF_SHUTR|BF_READ_TIMEOUT|BF_READ_ACTIVITY|BF_READ_NOEXP))) &&
|
|
unlikely(tick_is_expired(b->rex, now_ms)))
|
|
b->flags |= BF_READ_TIMEOUT;
|
|
|
|
if (likely(!(b->flags & (BF_SHUTW|BF_WRITE_TIMEOUT|BF_WRITE_ACTIVITY))) &&
|
|
unlikely(tick_is_expired(b->wex, now_ms)))
|
|
b->flags |= BF_WRITE_TIMEOUT;
|
|
|
|
if (likely(!(b->flags & BF_ANA_TIMEOUT)) &&
|
|
unlikely(tick_is_expired(b->analyse_exp, now_ms)))
|
|
b->flags |= BF_ANA_TIMEOUT;
|
|
}
|
|
|
|
/* Schedule all remaining buffer data to be sent. ->o is not touched if it
|
|
* already covers those data. That permits doing a flush even after a forward,
|
|
* although not recommended.
|
|
*/
|
|
static inline void buffer_flush(struct channel *buf)
|
|
{
|
|
buf->buf.p = buffer_wrap_add(&buf->buf, buf->buf.p + buf->buf.i);
|
|
buf->buf.o += buf->buf.i;
|
|
buf->buf.i = 0;
|
|
if (buf->buf.o)
|
|
buf->flags &= ~BF_OUT_EMPTY;
|
|
}
|
|
|
|
/* Erase any content from buffer <buf> and adjusts flags accordingly. Note
|
|
* that any spliced data is not affected since we may not have any access to
|
|
* it.
|
|
*/
|
|
static inline void buffer_erase(struct channel *buf)
|
|
{
|
|
buf->buf.o = 0;
|
|
buf->buf.i = 0;
|
|
buf->to_forward = 0;
|
|
buf->buf.p = buf->buf.data;
|
|
buf->flags &= ~(BF_FULL | BF_OUT_EMPTY);
|
|
if (!buf->pipe)
|
|
buf->flags |= BF_OUT_EMPTY;
|
|
}
|
|
|
|
/* Cut the "tail" of the buffer, which means strip it to the length of unsent
|
|
* data only, and kill any remaining unsent data. Any scheduled forwarding is
|
|
* stopped. This is mainly to be used to send error messages after existing
|
|
* data.
|
|
*/
|
|
static inline void bi_erase(struct channel *buf)
|
|
{
|
|
if (!buf->buf.o)
|
|
return buffer_erase(buf);
|
|
|
|
buf->to_forward = 0;
|
|
if (!buf->buf.i)
|
|
return;
|
|
|
|
buf->buf.i = 0;
|
|
buf->flags &= ~BF_FULL;
|
|
if (bi_full(buf))
|
|
buf->flags |= BF_FULL;
|
|
}
|
|
|
|
/* Cut the first <n> pending bytes in a contiguous buffer. It is illegal to
|
|
* call this function with remaining data waiting to be sent (o > 0). The
|
|
* caller must ensure that <n> is smaller than the actual buffer's length.
|
|
* This is mainly used to remove empty lines at the beginning of a request
|
|
* or a response.
|
|
*/
|
|
static inline void bi_fast_delete(struct buffer *buf, int n)
|
|
{
|
|
buf->i -= n;
|
|
buf->p += n;
|
|
}
|
|
|
|
/* marks the buffer as "shutdown" ASAP for reads */
|
|
static inline void buffer_shutr_now(struct channel *buf)
|
|
{
|
|
buf->flags |= BF_SHUTR_NOW;
|
|
}
|
|
|
|
/* marks the buffer as "shutdown" ASAP for writes */
|
|
static inline void buffer_shutw_now(struct channel *buf)
|
|
{
|
|
buf->flags |= BF_SHUTW_NOW;
|
|
}
|
|
|
|
/* marks the buffer as "shutdown" ASAP in both directions */
|
|
static inline void buffer_abort(struct channel *buf)
|
|
{
|
|
buf->flags |= BF_SHUTR_NOW | BF_SHUTW_NOW;
|
|
buf->flags &= ~BF_AUTO_CONNECT;
|
|
}
|
|
|
|
/* Installs <func> as a hijacker on the buffer <b> for session <s>. The hijack
|
|
* flag is set, and the function called once. The function is responsible for
|
|
* clearing the hijack bit. It is possible that the function clears the flag
|
|
* during this first call.
|
|
*/
|
|
static inline void buffer_install_hijacker(struct session *s,
|
|
struct channel *b,
|
|
void (*func)(struct session *, struct channel *))
|
|
{
|
|
b->hijacker = func;
|
|
b->flags |= BF_HIJACK;
|
|
func(s, b);
|
|
}
|
|
|
|
/* Releases the buffer from hijacking mode. Often used by the hijack function */
|
|
static inline void buffer_stop_hijack(struct channel *buf)
|
|
{
|
|
buf->flags &= ~BF_HIJACK;
|
|
}
|
|
|
|
/* allow the consumer to try to establish a new connection. */
|
|
static inline void buffer_auto_connect(struct channel *buf)
|
|
{
|
|
buf->flags |= BF_AUTO_CONNECT;
|
|
}
|
|
|
|
/* prevent the consumer from trying to establish a new connection, and also
|
|
* disable auto shutdown forwarding.
|
|
*/
|
|
static inline void buffer_dont_connect(struct channel *buf)
|
|
{
|
|
buf->flags &= ~(BF_AUTO_CONNECT|BF_AUTO_CLOSE);
|
|
}
|
|
|
|
/* allow the producer to forward shutdown requests */
|
|
static inline void buffer_auto_close(struct channel *buf)
|
|
{
|
|
buf->flags |= BF_AUTO_CLOSE;
|
|
}
|
|
|
|
/* prevent the producer from forwarding shutdown requests */
|
|
static inline void buffer_dont_close(struct channel *buf)
|
|
{
|
|
buf->flags &= ~BF_AUTO_CLOSE;
|
|
}
|
|
|
|
/* allow the producer to read / poll the input */
|
|
static inline void buffer_auto_read(struct channel *buf)
|
|
{
|
|
buf->flags &= ~BF_DONT_READ;
|
|
}
|
|
|
|
/* prevent the producer from read / poll the input */
|
|
static inline void buffer_dont_read(struct channel *buf)
|
|
{
|
|
buf->flags |= BF_DONT_READ;
|
|
}
|
|
|
|
/*
|
|
* Tries to realign the given buffer, and returns how many bytes can be written
|
|
* there at once without overwriting anything.
|
|
*/
|
|
static inline int buffer_realign(struct buffer *buf)
|
|
{
|
|
if (!(buf->i | buf->o)) {
|
|
/* let's realign the buffer to optimize I/O */
|
|
buf->p = buf->data;
|
|
}
|
|
return buffer_contig_space(buf);
|
|
}
|
|
|
|
/*
|
|
* Advance the buffer's read pointer by <len> bytes. This is useful when data
|
|
* have been read directly from the buffer. It is illegal to call this function
|
|
* with <len> causing a wrapping at the end of the buffer. It's the caller's
|
|
* responsibility to ensure that <len> is never larger than buf->o.
|
|
*/
|
|
static inline void bo_skip(struct channel *buf, int len)
|
|
{
|
|
buf->buf.o -= len;
|
|
if (!buf->buf.o && !buf->pipe)
|
|
buf->flags |= BF_OUT_EMPTY;
|
|
|
|
if (buffer_len(&buf->buf) == 0)
|
|
buf->buf.p = buf->buf.data;
|
|
|
|
if (!bi_full(buf))
|
|
buf->flags &= ~BF_FULL;
|
|
|
|
/* notify that some data was written to the SI from the buffer */
|
|
buf->flags |= BF_WRITE_PARTIAL;
|
|
}
|
|
|
|
/* Tries to copy chunk <chunk> into buffer <buf> after length controls.
|
|
* The ->o and to_forward pointers are updated. If the buffer's input is
|
|
* closed, -2 is returned. If the block is too large for this buffer, -3 is
|
|
* returned. If there is not enough room left in the buffer, -1 is returned.
|
|
* Otherwise the number of bytes copied is returned (0 being a valid number).
|
|
* Buffer flags FULL, EMPTY and READ_PARTIAL are updated if some data can be
|
|
* transferred. The chunk's length is updated with the number of bytes sent.
|
|
*/
|
|
static inline int bi_putchk(struct channel *buf, struct chunk *chunk)
|
|
{
|
|
int ret;
|
|
|
|
ret = bi_putblk(buf, chunk->str, chunk->len);
|
|
if (ret > 0)
|
|
chunk->len -= ret;
|
|
return ret;
|
|
}
|
|
|
|
/* Tries to copy string <str> at once into buffer <buf> after length controls.
|
|
* The ->o and to_forward pointers are updated. If the buffer's input is
|
|
* closed, -2 is returned. If the block is too large for this buffer, -3 is
|
|
* returned. If there is not enough room left in the buffer, -1 is returned.
|
|
* Otherwise the number of bytes copied is returned (0 being a valid number).
|
|
* Buffer flags FULL, EMPTY and READ_PARTIAL are updated if some data can be
|
|
* transferred.
|
|
*/
|
|
static inline int bi_putstr(struct channel *buf, const char *str)
|
|
{
|
|
return bi_putblk(buf, str, strlen(str));
|
|
}
|
|
|
|
/*
|
|
* Return one char from the buffer. If the buffer is empty and closed, return -2.
|
|
* If the buffer is just empty, return -1. The buffer's pointer is not advanced,
|
|
* it's up to the caller to call bo_skip(buf, 1) when it has consumed the char.
|
|
* Also note that this function respects the ->o limit.
|
|
*/
|
|
static inline int bo_getchr(struct channel *buf)
|
|
{
|
|
/* closed or empty + imminent close = -2; empty = -1 */
|
|
if (unlikely(buf->flags & (BF_OUT_EMPTY|BF_SHUTW))) {
|
|
if (buf->flags & (BF_SHUTW|BF_SHUTW_NOW))
|
|
return -2;
|
|
return -1;
|
|
}
|
|
return *buffer_wrap_sub(&buf->buf, buf->buf.p - buf->buf.o);
|
|
}
|
|
|
|
/* 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, r, lr) 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 nor BF_OUT_EMPTY because
|
|
* it does not make sense to use it on data scheduled to be sent.
|
|
*/
|
|
static inline int buffer_replace(struct channel *b, char *pos, char *end, const char *str)
|
|
{
|
|
return buffer_replace2(b, pos, end, str, strlen(str));
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Functions below are used to manage chunks
|
|
*
|
|
*/
|
|
|
|
static inline void chunk_init(struct chunk *chk, char *str, size_t size) {
|
|
chk->str = str;
|
|
chk->len = 0;
|
|
chk->size = size;
|
|
}
|
|
|
|
/* report 0 in case of error, 1 if OK. */
|
|
static inline int chunk_initlen(struct chunk *chk, char *str, size_t size, int len) {
|
|
|
|
if (size && len > size)
|
|
return 0;
|
|
|
|
chk->str = str;
|
|
chk->len = len;
|
|
chk->size = size;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static inline void chunk_initstr(struct chunk *chk, char *str) {
|
|
chk->str = str;
|
|
chk->len = strlen(str);
|
|
chk->size = 0; /* mark it read-only */
|
|
}
|
|
|
|
static inline int chunk_strcpy(struct chunk *chk, const char *str) {
|
|
size_t len;
|
|
|
|
len = strlen(str);
|
|
|
|
if (unlikely(len > chk->size))
|
|
return 0;
|
|
|
|
chk->len = len;
|
|
memcpy(chk->str, str, len);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int chunk_printf(struct chunk *chk, const char *fmt, ...)
|
|
__attribute__ ((format(printf, 2, 3)));
|
|
|
|
int chunk_htmlencode(struct chunk *dst, struct chunk *src);
|
|
int chunk_asciiencode(struct chunk *dst, struct chunk *src, char qc);
|
|
|
|
static inline void chunk_reset(struct chunk *chk) {
|
|
chk->str = NULL;
|
|
chk->len = -1;
|
|
chk->size = 0;
|
|
}
|
|
|
|
static inline void chunk_destroy(struct chunk *chk) {
|
|
|
|
if (!chk->size)
|
|
return;
|
|
|
|
if (chk->str)
|
|
free(chk->str);
|
|
|
|
chunk_reset(chk);
|
|
}
|
|
|
|
/*
|
|
* frees the destination chunk if already allocated, allocates a new string,
|
|
* and copies the source into it. The pointer to the destination string is
|
|
* returned, or NULL if the allocation fails or if any pointer is NULL..
|
|
*/
|
|
static inline char *chunk_dup(struct chunk *dst, const struct chunk *src) {
|
|
if (!dst || !src || !src->str)
|
|
return NULL;
|
|
if (dst->str)
|
|
free(dst->str);
|
|
dst->len = src->len;
|
|
dst->str = (char *)malloc(dst->len);
|
|
memcpy(dst->str, src->str, dst->len);
|
|
return dst->str;
|
|
}
|
|
|
|
#endif /* _PROTO_BUFFERS_H */
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-indent-level: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|