Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed Jul 3, 2024
1 parent 21939e3 commit 42dc18d
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 7 deletions.
20 changes: 16 additions & 4 deletions extras/test/ChunkDecodingStreamTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,21 @@ TEST_CASE("ChunkDecodingStream") {
StringStream upstream;
ChunkDecodingStream stream{upstream};

WHEN("upstream is empty") {
THEN("available() should return 0") {
CHECK(stream.available() == 0);
}
SUBCASE("empty stream") {
CHECK(stream.available() == 0);
CHECK(stream.peek() == -1);
CHECK(stream.read() == -1);
CHECK(stream.readString() == "");
}

SUBCASE("one chunk, no extension") {
upstream.print("3\r\nABC");
CHECK(stream.available() == 3);
CHECK(stream.peek() == 'A');
CHECK(stream.available() == 3);
CHECK(stream.read() == 'A');
CHECK(stream.available() == 2);
CHECK(stream.readString() == "BC");
CHECK(stream.available() == 0);
}
}
112 changes: 112 additions & 0 deletions src/StreamUtils/Policies/ChunkDecodingPolicy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// StreamUtils - github.com/bblanchon/ArduinoStreamUtils
// Copyright Benoit Blanchon 2019-2024
// MIT License

#pragma once

#include <Client.h>
#include <assert.h>

namespace StreamUtils {

class ChunkDecodingPolicy {
enum class State {
Size,
Extensions,
StartCrLf,
Body,
Error,
};

public:
int available(Stream &target) {
if (!isInChunkBody(target))
return 0;
return min(target.available(), remaining_);
}

int read(Stream &target) {
if (!isInChunkBody(target))
return -1;
int c = target.read();
if (c >= 0)
remaining_--;
return c;
}

int peek(Stream &target) {
if (!isInChunkBody(target))
return -1;
return target.peek();
}

size_t readBytes(Stream &target, char *buffer, size_t size) {
if (!isInChunkBody(target))
return 0;
size_t n = target.readBytes(buffer, min(size, remaining_));
remaining_ -= n;
return n;
}

int read(Client &target, uint8_t *buffer, size_t size) {
return readBytes(target, reinterpret_cast<char *>(buffer), size);
}

private:
bool isInChunkBody(Stream &target) {
while (state_ != State::Error && state_ != State::Body &&
target.available()) {
state_ = injestNext(target);
}
return state_ == State::Body;
}

State injestNext(Stream &target) {
int c = target.read();
switch (state_) {
case State::Size:
if (c >= '0' && c <= '9')
return appendSizeHexDigit(c - '0');
else if (c >= 'A' && c <= 'F')
return appendSizeHexDigit(c - 'A' + 10);
else if (c >= 'a' && c <= 'f')
return appendSizeHexDigit(c - 'a' + 10);
else if (c == '\r')
return State::StartCrLf;
else if (c == ' ' || c == '\t' || c == ';')
return State::Extensions;
else
return State::Error;

case State::Extensions:
if (c == '\r')
return State::StartCrLf;
else
return State::Extensions;

case State::StartCrLf:
if (c == '\n')
return State::Body;
else
return State::Error;

default:
assert(false);
return State::Error;
}
}

State appendSizeHexDigit(uint8_t digit) {
remaining_ = remaining_ * 16 + digit;
return State::Size;
}

size_t min(size_t a, size_t b) {
return a < b ? a : b;
}

size_t remaining_ = 0;
State state_ = State::Size;
};

} // namespace StreamUtils
6 changes: 3 additions & 3 deletions src/StreamUtils/Streams/ChunkDecodingStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@

#pragma once

#include "StreamUtils/Policies/ReadForwardingPolicy.hpp"
#include "StreamUtils/Policies/ChunkDecodingPolicy.hpp"
#include "StreamUtils/Policies/WriteForwardingPolicy.hpp"

#include "StreamProxy.hpp"

namespace StreamUtils {

struct ChunkDecodingStream
: StreamProxy<ReadForwardingPolicy, WriteForwardingPolicy> {
: StreamProxy<ChunkDecodingPolicy, WriteForwardingPolicy> {
ChunkDecodingStream(Stream &target)
: StreamProxy<ReadForwardingPolicy, WriteForwardingPolicy>(target) {}
: StreamProxy<ChunkDecodingPolicy, WriteForwardingPolicy>(target) {}
};

} // namespace StreamUtils

0 comments on commit 42dc18d

Please sign in to comment.