// -*- mode: c++; c-basic-offset: 2; -*- #include "common.hh" #include #include #include #include "chunked.hh" namespace { enum State { CHUNK, IN_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: if (static_cast(end - d) < size_) { return avail; } d += size_; 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; } 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_; }; } // namespace // static Chunked* Chunked::create() { return new ChunkedImpl(); }