• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bblanchon / ArduinoStreamUtils / 9872110687

10 Jul 2024 09:58AM CUT coverage: 98.468%. Remained the same
9872110687

push

github

bblanchon
Set version to 1.9.0

964 of 979 relevant lines covered (98.47%)

2069.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.26
/src/StreamUtils/Policies/ChunkDecodingPolicy.hpp
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
#include "../Helpers.hpp"
10

11
namespace StreamUtils {
12

13
class ChunkDecodingPolicy {
14
  enum class State {
15
    ChunkSize,
16
    ChunkExtensions,
17
    ChunkStart,
18
    ChunkBody,
19
    ChunkEndCr,
20
    ChunkEndLf,
21
    TrailerStart,
22
    Trailer,
23
    TrailerEnd,
24
    FinalCrLf,
25
    Ended,
26
    Error,
27
  };
28

29
 public:
30
  int available(Stream &target) {
276✔
31
    if (!goToChunkBody(target))
276✔
32
      return 0;
156✔
33
    return min_(target.available(), remaining_);
120✔
34
  }
35

36
  int read(Stream &target) {
1,203✔
37
    if (!goToChunkBody(target))
1,203✔
38
      return -1;
306✔
39
    int c = target.read();
897✔
40
    if (c >= 0)
897✔
41
      decreaseRemaining(1);
897✔
42
    return c;
897✔
43
  }
44

45
  int peek(Stream &target) {
60✔
46
    if (!goToChunkBody(target))
60✔
47
      return -1;
24✔
48
    return target.peek();
36✔
49
  }
50

51
  size_t readBytes(Stream &target, char *buffer, size_t size) {
36✔
52
    return doReadBytes(target, buffer, size);
36✔
53
  }
54

55
  int read(Client &target, uint8_t *buffer, size_t size) {
36✔
56
    return static_cast<int>(
57
        doReadBytes(target, reinterpret_cast<char *>(buffer), size));
36✔
58
  }
59

60
  bool error() const {
5,370✔
61
    return state_ == State::Error;
5,370✔
62
  }
63

64
  bool ended() const {
4,953✔
65
    return state_ == State::Ended;
4,953✔
66
  }
67

68
 private:
69
  template <typename TTarget>  // Stream or Client
70
  size_t doReadBytes(TTarget &target, char *buffer, size_t size) {
72✔
71
    size_t result = 0;
72✔
72
    while (size > 0 && !error() && !ended() && goToChunkBody(target, true)) {
159✔
73
      size_t n = readOrReadBytes(target, buffer, min_(size, remaining_));
87✔
74
      decreaseRemaining(n);
87✔
75
      result += n;
87✔
76
      size -= n;
87✔
77
      buffer += n;
87✔
78
    }
79
    return result;
72✔
80
  }
81

82
  bool inBody() const {
4,620✔
83
    return state_ == State::ChunkBody;
4,620✔
84
  }
85

86
  template <typename TTarget>  // Stream or Client
87
  bool goToChunkBody(TTarget &target, bool wait = false) {
1,698✔
88
    while (!error() && !ended() && !inBody()) {
5,007✔
89
      int c = readNextChar(target, wait);
3,480✔
90
      if (c < 0)
3,480✔
91
        return false;
171✔
92
      state_ = interpret(static_cast<char>(c));
3,309✔
93
    }
94
    return state_ == State::ChunkBody;
1,527✔
95
  }
96

97
  template <typename TTarget>  // Stream or Client
98
  int readNextChar(TTarget &target, bool wait = false) {
3,480✔
99
    if (wait) {
3,480✔
100
      char c;
101
      if (readOrReadBytes(target, &c, 1) == 1)
603✔
102
        return c;
561✔
103
      else
104
        return -1;
42✔
105
    } else {
106
      return target.read();
2,877✔
107
    }
108
  }
109

110
  State interpret(char c) {
3,309✔
111
    switch (state_) {
3,309✔
112
      case State::ChunkSize:
1,086✔
113
        if (c >= '0' && c <= '9')
1,086✔
114
          return appendSizeHexDigit(c - '0');
597✔
115
        else if (c >= 'A' && c <= 'F')
489✔
116
          return appendSizeHexDigit(c - 'A' + 10);
24✔
117
        else if (c >= 'a' && c <= 'f')
465✔
118
          return appendSizeHexDigit(c - 'a' + 10);
24✔
119
        else if (c == '\r')
441✔
120
          return State::ChunkStart;
357✔
121
        else if (c == ' ' || c == '\t' || c == ';')
84✔
122
          return State::ChunkExtensions;
48✔
123
        else
124
          return State::Error;
36✔
125

126
      case State::ChunkExtensions:
408✔
127
        if (c == '\r')
408✔
128
          return State::ChunkStart;
48✔
129
        else
130
          return State::ChunkExtensions;
360✔
131

132
      case State::ChunkStart:
405✔
133
        if (c == '\n') {
405✔
134
          if (remaining_ == 0)
393✔
135
            return State::TrailerStart;
123✔
136
          else
137
            return State::ChunkBody;
270✔
138
        } else
139
          return State::Error;
12✔
140

141
      case State::ChunkEndCr:
270✔
142
        if (c == '\r')
270✔
143
          return State::ChunkEndLf;
234✔
144
        else
145
          return State::Error;
36✔
146

147
      case State::ChunkEndLf:
234✔
148
        if (c == '\n')
234✔
149
          return State::ChunkSize;
222✔
150
        else
151
          return State::Error;
12✔
152

153
      case State::TrailerStart:
171✔
154
        if (c == '\r')
171✔
155
          return State::FinalCrLf;
99✔
156
        else
157
          return State::Trailer;
72✔
158

159
      case State::Trailer:
576✔
160
        if (c == '\r')
576✔
161
          return State::TrailerEnd;
60✔
162
        else if (c == '\n')
516✔
163
          return State::Error;
12✔
164
        else
165
          return State::Trailer;
504✔
166

167
      case State::TrailerEnd:
60✔
168
        if (c == '\n')
60✔
169
          return State::TrailerStart;
48✔
170
        else
171
          return State::Error;
12✔
172

173
      case State::FinalCrLf:
99✔
174
        if (c == '\n') {
99✔
175
          assert(remaining_ == 0);
87✔
176
          return State::Ended;
87✔
177
        } else
178
          return State::Error;
12✔
179

180
      default:
×
181
        return State::Error;
×
182
    }
183
  }
184

185
  State appendSizeHexDigit(uint8_t digit) {
645✔
186
    auto oldRemaining = remaining_;
645✔
187
    remaining_ = oldRemaining * 16 + digit;
645✔
188
    if (remaining_ < oldRemaining) {  // overflow
645✔
189
      remaining_ = 0;
12✔
190
      return State::Error;
12✔
191
    }
192
    return State::ChunkSize;
633✔
193
  }
194

195
  void decreaseRemaining(size_t n) {
984✔
196
    assert(remaining_ >= n);
984✔
197
    remaining_ -= n;
984✔
198
    if (remaining_ == 0)
984✔
199
      state_ = State::ChunkEndCr;
270✔
200
  }
984✔
201

202
  size_t min_(size_t a, size_t b) {
207✔
203
    return a < b ? a : b;
207✔
204
  }
205

206
  size_t remaining_ = 0;
207
  State state_ = State::ChunkSize;
208
};
209

210
}  // namespace StreamUtils
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc