/* * PgBouncer - Lightweight connection pooler for PostgreSQL. * * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Temporary buffer for single i/o. * * Pattern: * * iobuf_get_and_reset() * start: * iobuf_recv() * loop: * if (new_pkt) * iobuf_parse() * * if (send) { * iobuf_tag_send() * } else { * send_pending() * iobuf_tag_skip() * } * if (more-unparsed) * goto loop; * send_pending(); */ /* * 0 .. done_pos -- sent * done_pos .. parse_pos -- parsed, to send * parse_pos .. recv_pos -- received, to parse */ struct iobuf { unsigned done_pos; unsigned parse_pos; unsigned recv_pos; uint8_t buf[FLEX_ARRAY]; }; typedef struct iobuf IOBuf; static inline bool iobuf_sane(const IOBuf *io) { return (io == NULL) || ( io->parse_pos >= io->done_pos && io->recv_pos >= io->parse_pos && (unsigned)cf_sbuf_len >= io->recv_pos); } static inline bool iobuf_empty(const IOBuf *io) { return io == NULL || io->done_pos == io->recv_pos; } /* unsent amount */ static inline unsigned iobuf_amount_pending(const IOBuf *buf) { return buf->parse_pos - buf->done_pos; } /* max possible to parse (tag_send/tag_skip) */ static inline unsigned iobuf_amount_parse(const IOBuf *buf) { return buf->recv_pos - buf->parse_pos; } /* max possible to recv */ static inline unsigned iobuf_amount_recv(const IOBuf *buf) { return cf_sbuf_len - buf->recv_pos; } /* put all unparsed to mbuf */ static inline unsigned iobuf_parse_all(const IOBuf *buf, struct MBuf *mbuf) { unsigned avail = iobuf_amount_parse(buf); const uint8_t *pos = buf->buf + buf->parse_pos; mbuf_init_fixed_reader(mbuf, pos, avail); return avail; } /* put all unparsed to mbuf, with size limit */ static inline unsigned iobuf_parse_limit(const IOBuf *buf, struct MBuf *mbuf, unsigned limit) { unsigned avail = iobuf_amount_parse(buf); const uint8_t *pos = buf->buf + buf->parse_pos; if (avail > limit) avail = limit; mbuf_init_fixed_reader(mbuf, pos, avail); return avail; } static inline void iobuf_tag_send(IOBuf *io, unsigned len) { Assert(len > 0 && len <= iobuf_amount_parse(io)); io->parse_pos += len; } static inline void iobuf_tag_skip(IOBuf *io, unsigned len) { Assert(io->parse_pos == io->done_pos); /* no send pending */ Assert(len > 0 && len <= iobuf_amount_parse(io)); io->parse_pos += len; io->done_pos = io->parse_pos; } static inline void iobuf_try_resync(IOBuf *io, unsigned small_pkt) { unsigned avail = io->recv_pos - io->done_pos; if (avail == 0) { if (io->recv_pos > 0) io->recv_pos = io->parse_pos = io->done_pos = 0; } else if (avail <= small_pkt && io->done_pos > 0) { memmove(io->buf, io->buf + io->done_pos, avail); io->parse_pos -= io->done_pos; io->recv_pos = avail; io->done_pos = 0; } } static inline void iobuf_reset(IOBuf *io) { io->recv_pos = io->parse_pos = io->done_pos = 0; }