summaryrefslogtreecommitdiff
path: root/src/chunked.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/chunked.cc')
-rw-r--r--src/chunked.cc106
1 files changed, 106 insertions, 0 deletions
diff --git a/src/chunked.cc b/src/chunked.cc
new file mode 100644
index 0000000..99aea0d
--- /dev/null
+++ b/src/chunked.cc
@@ -0,0 +1,106 @@
+// -*- mode: c++; c-basic-offset: 2; -*-
+
+#include "common.hh"
+
+#include <cerrno>
+#include <cstdint>
+#include <cstdlib>
+
+#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<char const*>(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<uint64_t>(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();
+}
+