#pragma once
#include "common.hpp"
#include "pool.hpp"
class buf_t {
private:
char buf[BUF_SIZE];
size_t off;
size_t len;
public:
buf_t* next;
INLINE buf_t(): off(0), len(0), next(NULL) {}
INLINE bool has_space() const {
return len < sizeof(buf);
}
INLINE bool has_data() const {
return off != len;
}
INLINE void get_space(char*& b, size_t& l) {
assert(has_space());
b = buf+len;
l = sizeof(buf)-len;
}
INLINE size_t get_data() {
return len - off;
}
INLINE void get_data(char*& b, size_t& l) {
assert(has_data());
b = buf+off;
l = len-off;
}
INLINE void get_data(const char*& b, size_t& l) const {
assert(has_data());
b = buf+off;
l = len-off;
}
INLINE void del_data(size_t l) {
off += l;
assert(off <= len);
}
INLINE void add_data(size_t l) {
len += l;
assert(len <= sizeof(buf));
}
INLINE bool add_nul() {
if (!has_space()) return false;
buf[len++] = '\0';
return true;
}
INLINE bool is_done() const {
return off == sizeof(buf) && len == sizeof(buf);
}
INLINE bool is_done(size_t l) const {
return off+l == sizeof(buf) && len == sizeof(buf);
}
INLINE void rewind() { ///< revert del_data()
off = 0;
}
INLINE void reset() {
off = 0;
len = 0;
}
void debug();
};
extern CPoolPTS<buf_t> buf_pool; ///< threadsafe due to @see AioSink::cb
class chain_t {
private:
buf_t* head;
buf_t* tail;
size_t bufs;
bool is_initial_data; ///< whether we still have the very first byte
buf_t* pop_head();
tristate_t startswith(const char*, size_t) const;
public:
INLINE chain_t(): head(NULL), tail(NULL), bufs(0), is_initial_data(true) {
}
INLINE ~chain_t() {
reset();
}
INLINE void reset() {
unless (bufs) return;
bufs = 0;
tail = NULL;
// nope: is_initial_data = true;
buf_t* node = head;
while (node) {
head = head->next;
buf_pool.push(node);
node = head;
}
}
INLINE bool is_empty() const { ///< whether get_data() will succeed
return !head || !head->has_data();
}
INLINE bool is_full() const { ///< whether get_space() or add_data(buf_t*) will succeed
return bufs >= BUF_MAX && !tail->has_space();
}
INLINE bool get_data(char*& b, size_t& l) {
if (head && head->has_data()) {
head->get_data(b, l);
return true;
} else {
return false;
}
}
INLINE bool get_data(const char*& b, size_t& l) const {
if (head && head->has_data()) {
head->get_data(b, l);
return true;
} else {
return false;
}
}
INLINE bool get_space(char*& b, size_t& l) {
if (tail && tail->has_space()) {
tail->get_space(b, l);
return true;
} else {
return false;
}
}
INLINE buf_t* del_data(size_t l) { ///< how much of a previous get_data() has been written. if the whole buf has been written, remove and return it.
assert(head && head->has_data());
if (likely(l)) is_initial_data = false;
head->del_data(l);
if (head->is_done()) {
head->rewind();
return pop_head();
} else {
return NULL;
}
}
INLINE buf_t* will_del_data(size_t l) { ///< what del_data() would return, but without actually doing it
assert(head && head->has_data());
return head->is_done(l)? head: NULL;
}
INLINE void add_data(size_t l) { ///< how much of a previous get_space() has been written
assert(tail && tail->has_space());
tail->add_data(l);
}
INLINE void add_data(buf_t* b) { ///< push a new buf with initial data, in case get_space() failed. assumes !is_full().
assert(b->has_data());
if (!tail) {
assert(!bufs && !head);
head = b;
tail = b;
} else {
assert(!tail->has_space()); // as get_space() has failed
tail->next = b;
tail = b;
}
assert(bufs < BUF_MAX);
b->next = NULL;
++bufs;
}
bool add_data(chain_t*); ///< try to clear other chain and take its content. already written/deleted but still available data will be restored. ignores is_full().
void add_data(const char*, size_t); ///< copy from buffer and append. ignores is_full().
//void prepend(chain_t*); ///< clears other chain and takes its content before our own. ignores is_full().
void prepend(buf_t*); ///< inserts buffer at head i.e. as initial data. ignores size checks.
tristate_t is_http_req() const; ///< true if initial data is available and its actually "GET /"
tristate_t is_http_resp() const; ///< true if initial data is available and its actually "HTTP/1"
void debug();
};