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

bblanchon / ArduinoStreamUtils / 9818243506

06 Jul 2024 09:18AM UTC coverage: 98.457% (+0.1%) from 98.323%
9818243506

push

github

bblanchon
Rename min

3 of 3 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

957 of 972 relevant lines covered (98.46%)

2001.05 hits per line

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

98.23
/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) {
56
    return static_cast<int>(
57
        doReadBytes(target, reinterpret_cast<char *>(buffer), size));
58
  }
59

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

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

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

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

86
  bool goToChunkBody(Stream &target, bool wait = false) {
1,614✔
87
    while (!error() && !ended() && !inBody()) {
4,563✔
88
      int c = readNextChar(target, wait);
3,108✔
89
      if (c < 0)
3,108✔
90
        return false;
159✔
91
      state_ = interpret(static_cast<char>(c));
2,949✔
92
    }
93
    return state_ == State::ChunkBody;
1,455✔
94
  }
95

96
  int readNextChar(Stream &target, bool wait = false) {
3,108✔
97
    if (wait) {
3,108✔
98
      char c;
99
      if (target.readBytes(&c, 1) == 1)
231✔
100
        return c;
201✔
101
      else
102
        return -1;
30✔
103
    } else {
104
      return target.read();
2,877✔
105
    }
106
  }
107

108
  State interpret(char c) {
2,949✔
109
    switch (state_) {
2,949✔
110
      case State::ChunkSize:
942✔
111
        if (c >= '0' && c <= '9')
942✔
112
          return appendSizeHexDigit(c - '0');
525✔
113
        else if (c >= 'A' && c <= 'F')
417✔
114
          return appendSizeHexDigit(c - 'A' + 10);
24✔
115
        else if (c >= 'a' && c <= 'f')
393✔
116
          return appendSizeHexDigit(c - 'a' + 10);
24✔
117
        else if (c == '\r')
369✔
118
          return State::ChunkStart;
285✔
119
        else if (c == ' ' || c == '\t' || c == ';')
84✔
120
          return State::ChunkExtensions;
48✔
121
        else
122
          return State::Error;
36✔
123

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

130
      case State::ChunkStart:
333✔
131
        if (c == '\n') {
333✔
132
          if (remaining_ == 0)
321✔
133
            return State::TrailerStart;
99✔
134
          else
135
            return State::ChunkBody;
222✔
136
        } else
137
          return State::Error;
12✔
138

139
      case State::ChunkEndCr:
222✔
140
        if (c == '\r')
222✔
141
          return State::ChunkEndLf;
186✔
142
        else
143
          return State::Error;
36✔
144

145
      case State::ChunkEndLf:
186✔
146
        if (c == '\n')
186✔
147
          return State::ChunkSize;
174✔
148
        else
149
          return State::Error;
12✔
150

151
      case State::TrailerStart:
147✔
152
        if (c == '\r')
147✔
153
          return State::FinalCrLf;
75✔
154
        else
155
          return State::Trailer;
72✔
156

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

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

171
      case State::FinalCrLf:
75✔
172
        if (c == '\n') {
75✔
173
          assert(remaining_ == 0);
63✔
174
          return State::Ended;
63✔
175
        } else
176
          return State::Error;
12✔
177

UNCOV
178
      default:
×
UNCOV
179
        return State::Error;
×
180
    }
181
  }
182

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

193
  void decreaseRemaining(size_t n) {
936✔
194
    assert(remaining_ >= n);
936✔
195
    remaining_ -= n;
936✔
196
    if (remaining_ == 0)
936✔
197
      state_ = State::ChunkEndCr;
222✔
198
  }
936✔
199

200
  size_t min_(size_t a, size_t b) {
159✔
201
    return a < b ? a : b;
159✔
202
  }
203

204
  size_t remaining_ = 0;
205
  State state_ = State::ChunkSize;
206
};
207

208
}  // 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

© 2026 Coveralls, Inc