Skip to content

Commit 42dc18d

Browse files
committed
Initial implementation
1 parent 21939e3 commit 42dc18d

File tree

3 files changed

+131
-7
lines changed

3 files changed

+131
-7
lines changed

extras/test/ChunkDecodingStreamTest.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,21 @@ TEST_CASE("ChunkDecodingStream") {
1313
StringStream upstream;
1414
ChunkDecodingStream stream{upstream};
1515

16-
WHEN("upstream is empty") {
17-
THEN("available() should return 0") {
18-
CHECK(stream.available() == 0);
19-
}
16+
SUBCASE("empty stream") {
17+
CHECK(stream.available() == 0);
18+
CHECK(stream.peek() == -1);
19+
CHECK(stream.read() == -1);
20+
CHECK(stream.readString() == "");
21+
}
22+
23+
SUBCASE("one chunk, no extension") {
24+
upstream.print("3\r\nABC");
25+
CHECK(stream.available() == 3);
26+
CHECK(stream.peek() == 'A');
27+
CHECK(stream.available() == 3);
28+
CHECK(stream.read() == 'A');
29+
CHECK(stream.available() == 2);
30+
CHECK(stream.readString() == "BC");
31+
CHECK(stream.available() == 0);
2032
}
2133
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// StreamUtils - github.com/bblanchon/ArduinoStreamUtils
2+
// Copyright Benoit Blanchon 2019-2024
3+
// MIT License
4+
5+
#pragma once
6+
7+
#include <Client.h>
8+
#include <assert.h>
9+
10+
namespace StreamUtils {
11+
12+
class ChunkDecodingPolicy {
13+
enum class State {
14+
Size,
15+
Extensions,
16+
StartCrLf,
17+
Body,
18+
Error,
19+
};
20+
21+
public:
22+
int available(Stream &target) {
23+
if (!isInChunkBody(target))
24+
return 0;
25+
return min(target.available(), remaining_);
26+
}
27+
28+
int read(Stream &target) {
29+
if (!isInChunkBody(target))
30+
return -1;
31+
int c = target.read();
32+
if (c >= 0)
33+
remaining_--;
34+
return c;
35+
}
36+
37+
int peek(Stream &target) {
38+
if (!isInChunkBody(target))
39+
return -1;
40+
return target.peek();
41+
}
42+
43+
size_t readBytes(Stream &target, char *buffer, size_t size) {
44+
if (!isInChunkBody(target))
45+
return 0;
46+
size_t n = target.readBytes(buffer, min(size, remaining_));
47+
remaining_ -= n;
48+
return n;
49+
}
50+
51+
int read(Client &target, uint8_t *buffer, size_t size) {
52+
return readBytes(target, reinterpret_cast<char *>(buffer), size);
53+
}
54+
55+
private:
56+
bool isInChunkBody(Stream &target) {
57+
while (state_ != State::Error && state_ != State::Body &&
58+
target.available()) {
59+
state_ = injestNext(target);
60+
}
61+
return state_ == State::Body;
62+
}
63+
64+
State injestNext(Stream &target) {
65+
int c = target.read();
66+
switch (state_) {
67+
case State::Size:
68+
if (c >= '0' && c <= '9')
69+
return appendSizeHexDigit(c - '0');
70+
else if (c >= 'A' && c <= 'F')
71+
return appendSizeHexDigit(c - 'A' + 10);
72+
else if (c >= 'a' && c <= 'f')
73+
return appendSizeHexDigit(c - 'a' + 10);
74+
else if (c == '\r')
75+
return State::StartCrLf;
76+
else if (c == ' ' || c == '\t' || c == ';')
77+
return State::Extensions;
78+
else
79+
return State::Error;
80+
81+
case State::Extensions:
82+
if (c == '\r')
83+
return State::StartCrLf;
84+
else
85+
return State::Extensions;
86+
87+
case State::StartCrLf:
88+
if (c == '\n')
89+
return State::Body;
90+
else
91+
return State::Error;
92+
93+
default:
94+
assert(false);
95+
return State::Error;
96+
}
97+
}
98+
99+
State appendSizeHexDigit(uint8_t digit) {
100+
remaining_ = remaining_ * 16 + digit;
101+
return State::Size;
102+
}
103+
104+
size_t min(size_t a, size_t b) {
105+
return a < b ? a : b;
106+
}
107+
108+
size_t remaining_ = 0;
109+
State state_ = State::Size;
110+
};
111+
112+
} // namespace StreamUtils

src/StreamUtils/Streams/ChunkDecodingStream.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44

55
#pragma once
66

7-
#include "StreamUtils/Policies/ReadForwardingPolicy.hpp"
7+
#include "StreamUtils/Policies/ChunkDecodingPolicy.hpp"
88
#include "StreamUtils/Policies/WriteForwardingPolicy.hpp"
99

1010
#include "StreamProxy.hpp"
1111

1212
namespace StreamUtils {
1313

1414
struct ChunkDecodingStream
15-
: StreamProxy<ReadForwardingPolicy, WriteForwardingPolicy> {
15+
: StreamProxy<ChunkDecodingPolicy, WriteForwardingPolicy> {
1616
ChunkDecodingStream(Stream &target)
17-
: StreamProxy<ReadForwardingPolicy, WriteForwardingPolicy>(target) {}
17+
: StreamProxy<ChunkDecodingPolicy, WriteForwardingPolicy>(target) {}
1818
};
1919

2020
} // namespace StreamUtils

0 commit comments

Comments
 (0)