// -*- mode: c++; c-basic-offset: 2; -*- #include "common.hh" #include #include #include #include "chunked.hh" namespace { enum State { CHUNK, IN_CHUNK, END_CHUNK, TRAILER, DONE, }; class ChunkedImpl : public Chunked { public: ChunkedImpl() : state_(CHUNK), good_(true) { } size_t add(void const* data, size_t avail) override { if (!good_) return 0; auto const start = reinterpret_cast(data); auto const end = start + avail; auto d = start; while (true) { if (d == end) return avail; switch (state_) { case CHUNK: { auto p = find_crlf(d, end); if (!p) return d - start; char* x = nullptr; errno = 0; auto tmp = strtoull(d, &x, 16); if (errno || (x != p && (!x || *x != ';'))) { good_ = false; return d - start; } size_ = tmp; d = p + 2; if (size_ == 0) { // Last chunk state_ = TRAILER; } else { state_ = IN_CHUNK; } break; } case IN_CHUNK: { uint64_t left = end - d; if (left < size_) { this->data(d, left); size_ -= left; return avail; } this->data(d, size_); d += size_; state_ = END_CHUNK; break; } case END_CHUNK: { auto p = find_crlf(d, end); if (!p) return d - start; if (p != d) { good_ = false; return d - start; } d = p + 2; state_ = CHUNK; break; } case TRAILER: { auto p = find_crlf(d, end); if (!p) return d - start; if (p == d) { state_ = DONE; } d = p + 2; break; } case DONE: return d - start; } } } bool good() const override { return good_; } bool eof() const override { return state_ == DONE; } protected: virtual void data(void const* UNUSED(data), size_t UNUSED(size)) { } private: char const* find_crlf(char const* start, char const* end) { for (; start != end; ++start) { if (*start == '\r') { if (start + 1 == end) break; if (start[1] == '\n') return start; } } return nullptr; } State state_; bool good_; uint64_t size_; }; class ChunkedCallbackImpl : public ChunkedImpl { public: ChunkedCallbackImpl(DataCallback const& callback) : callback_(callback) { } void data(void const* data, size_t size) override { callback_(data, size); } private: DataCallback const callback_; }; } // namespace // static Chunked* Chunked::create() { return new ChunkedImpl(); } // static Chunked* Chunked::create(DataCallback const& callback) { return new ChunkedCallbackImpl(callback); }