mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-05 19:52:14 +00:00
ea1b06d5bb
The new file istbuf.h links the indirect strings (ist) with the buffers. The purpose is to encourage addition of more standard buffer manipulation functions that rely on this in order to improve the overall ease of use along all the code. Just like ist.h and buf.h, this new file is not expected to depend on anything beyond these two files. A few functions were added and/or converted from buffer.h : - b_isteq() : indicates if a buffer and a string match - b_isteat() : consumes a string from the buffer if it matches - b_istput() : appends a small string to a buffer (all or none) - b_putist() : appends part of a large string to a buffer The equivalent functions were removed from buffer.h and changed at the various call places.
263 lines
8.0 KiB
C
263 lines
8.0 KiB
C
/*
|
|
* include/common/buffer.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 _COMMON_BUFFER_H
|
|
#define _COMMON_BUFFER_H
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <common/buf.h>
|
|
#include <common/chunk.h>
|
|
#include <common/config.h>
|
|
#include <common/ist.h>
|
|
#include <common/istbuf.h>
|
|
#include <common/memory.h>
|
|
|
|
|
|
/* an element of the <buffer_wq> list. It represents an object that need to
|
|
* acquire a buffer to continue its process. */
|
|
struct buffer_wait {
|
|
void *target; /* The waiting object that should be woken up */
|
|
int (*wakeup_cb)(void *); /* The function used to wake up the <target>, passed as argument */
|
|
struct list list; /* Next element in the <buffer_wq> list */
|
|
};
|
|
|
|
extern struct pool_head *pool_head_buffer;
|
|
extern struct buffer buf_empty;
|
|
extern struct buffer buf_wanted;
|
|
extern struct list buffer_wq;
|
|
__decl_hathreads(extern HA_SPINLOCK_T buffer_wq_lock);
|
|
|
|
int init_buffer();
|
|
void deinit_buffer();
|
|
int buffer_replace2(struct buffer *b, char *pos, char *end, const char *str, int len);
|
|
int buffer_insert_line2(struct buffer *b, char *pos, const char *str, int len);
|
|
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
|
|
|
/*****************************************************************/
|
|
/* These functions are used to compute various buffer area sizes */
|
|
/*****************************************************************/
|
|
|
|
|
|
|
|
/***** FIXME: OLD API BELOW *****/
|
|
|
|
/* Normalizes a pointer after an addition */
|
|
static inline char *buffer_wrap_add(const struct buffer *buf, char *ptr)
|
|
{
|
|
if (ptr - buf->size >= b_orig(buf))
|
|
ptr -= buf->size;
|
|
return ptr;
|
|
}
|
|
|
|
/* 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 < b_orig(buf))
|
|
ptr += buf->size;
|
|
else if (ptr - buf->size >= b_orig(buf))
|
|
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;
|
|
|
|
count += count < 0 ? buf->size : 0;
|
|
return count;
|
|
}
|
|
|
|
/* 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 (buf == &buf_empty)
|
|
return 0;
|
|
|
|
return b_almost_full(buf);
|
|
}
|
|
|
|
/* 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 because it does not make sense
|
|
* to use it on data scheduled to be sent.
|
|
*/
|
|
static inline int buffer_replace(struct buffer *b, char *pos, char *end, const char *str)
|
|
{
|
|
return buffer_replace2(b, pos, end, str, strlen(str));
|
|
}
|
|
|
|
/* Allocates a buffer and replaces *buf with this buffer. If no memory is
|
|
* available, &buf_wanted is used instead. No control is made to check if *buf
|
|
* already pointed to another buffer. The allocated buffer is returned, or
|
|
* NULL in case no memory is available.
|
|
*/
|
|
static inline struct buffer *b_alloc(struct buffer **buf)
|
|
{
|
|
struct buffer *b;
|
|
|
|
*buf = &buf_wanted;
|
|
b = pool_alloc_dirty(pool_head_buffer);
|
|
if (likely(b)) {
|
|
b->size = pool_head_buffer->size - sizeof(struct buffer);
|
|
b_reset(b);
|
|
*buf = b;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
/* Allocates a buffer and replaces *buf with this buffer. If no memory is
|
|
* available, &buf_wanted is used instead. No control is made to check if *buf
|
|
* already pointed to another buffer. The allocated buffer is returned, or
|
|
* NULL in case no memory is available. The difference with b_alloc() is that
|
|
* this function only picks from the pool and never calls malloc(), so it can
|
|
* fail even if some memory is available.
|
|
*/
|
|
static inline struct buffer *b_alloc_fast(struct buffer **buf)
|
|
{
|
|
struct buffer *b;
|
|
|
|
*buf = &buf_wanted;
|
|
b = pool_get_first(pool_head_buffer);
|
|
if (likely(b)) {
|
|
b->size = pool_head_buffer->size - sizeof(struct buffer);
|
|
b_reset(b);
|
|
*buf = b;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
/* Releases buffer *buf (no check of emptiness) */
|
|
static inline void __b_drop(struct buffer **buf)
|
|
{
|
|
pool_free(pool_head_buffer, *buf);
|
|
}
|
|
|
|
/* Releases buffer *buf if allocated. */
|
|
static inline void b_drop(struct buffer **buf)
|
|
{
|
|
if (!(*buf)->size)
|
|
return;
|
|
__b_drop(buf);
|
|
}
|
|
|
|
/* Releases buffer *buf if allocated, and replaces it with &buf_empty. */
|
|
static inline void b_free(struct buffer **buf)
|
|
{
|
|
b_drop(buf);
|
|
*buf = &buf_empty;
|
|
}
|
|
|
|
/* Ensures that <buf> is allocated. If an allocation is needed, it ensures that
|
|
* there are still at least <margin> buffers available in the pool after this
|
|
* allocation so that we don't leave the pool in a condition where a session or
|
|
* a response buffer could not be allocated anymore, resulting in a deadlock.
|
|
* This means that we sometimes need to try to allocate extra entries even if
|
|
* only one buffer is needed.
|
|
*
|
|
* We need to lock the pool here to be sure to have <margin> buffers available
|
|
* after the allocation, regardless how many threads that doing it in the same
|
|
* time. So, we use internal and lockless memory functions (prefixed with '__').
|
|
*/
|
|
static inline struct buffer *b_alloc_margin(struct buffer **buf, int margin)
|
|
{
|
|
struct buffer *b;
|
|
|
|
if ((*buf)->size)
|
|
return *buf;
|
|
|
|
*buf = &buf_wanted;
|
|
#ifndef CONFIG_HAP_LOCKLESS_POOLS
|
|
HA_SPIN_LOCK(POOL_LOCK, &pool_head_buffer->lock);
|
|
#endif
|
|
|
|
/* fast path */
|
|
if ((pool_head_buffer->allocated - pool_head_buffer->used) > margin) {
|
|
b = __pool_get_first(pool_head_buffer);
|
|
if (likely(b)) {
|
|
#ifndef CONFIG_HAP_LOCKLESS_POOLS
|
|
HA_SPIN_UNLOCK(POOL_LOCK, &pool_head_buffer->lock);
|
|
#endif
|
|
b->size = pool_head_buffer->size - sizeof(struct buffer);
|
|
b_reset(b);
|
|
*buf = b;
|
|
return b;
|
|
}
|
|
}
|
|
|
|
/* slow path, uses malloc() */
|
|
b = __pool_refill_alloc(pool_head_buffer, margin);
|
|
|
|
#ifndef CONFIG_HAP_LOCKLESS_POOLS
|
|
HA_SPIN_UNLOCK(POOL_LOCK, &pool_head_buffer->lock);
|
|
#endif
|
|
|
|
if (b) {
|
|
b->size = pool_head_buffer->size - sizeof(struct buffer);
|
|
b_reset(b);
|
|
*buf = b;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
/* Offer a buffer currently belonging to target <from> to whoever needs one.
|
|
* Any pointer is valid for <from>, including NULL. Its purpose is to avoid
|
|
* passing a buffer to oneself in case of failed allocations (e.g. need two
|
|
* buffers, get one, fail, release it and wake up self again). In case of
|
|
* normal buffer release where it is expected that the caller is not waiting
|
|
* for a buffer, NULL is fine.
|
|
*/
|
|
void __offer_buffer(void *from, unsigned int threshold);
|
|
|
|
static inline void offer_buffers(void *from, unsigned int threshold)
|
|
{
|
|
HA_SPIN_LOCK(BUF_WQ_LOCK, &buffer_wq_lock);
|
|
if (LIST_ISEMPTY(&buffer_wq)) {
|
|
HA_SPIN_UNLOCK(BUF_WQ_LOCK, &buffer_wq_lock);
|
|
return;
|
|
}
|
|
__offer_buffer(from, threshold);
|
|
HA_SPIN_UNLOCK(BUF_WQ_LOCK, &buffer_wq_lock);
|
|
}
|
|
|
|
|
|
#endif /* _COMMON_BUFFER_H */
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-indent-level: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|