blob: 99aea0d0a4a470f1695aa7f37507f3ffe52fc8ba (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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();
}
|