Skip to content

Instantly share code, notes, and snippets.

@nlyan
Created October 10, 2024 07:18
Show Gist options
  • Save nlyan/43834f8bd339b5869b1ac043dc306eab to your computer and use it in GitHub Desktop.
Save nlyan/43834f8bd339b5869b1ac043dc306eab to your computer and use it in GitHub Desktop.
#include <cstring>
#include <string_view>
#include <tuple>
#include <charconv>
#include <iostream>
template <typename CRTP>
class UnbufferedPushDecoder
{
public:
bool write (std::string_view buffer);
};
template <typename CRTP>
bool
UnbufferedPushDecoder<CRTP>::write(std::string_view buffer)
{
std::size_t bytes_consumed = 0, min_bytes_needed_next = 0;
do
{
auto [bytes_consumed, min_bytes_needed_next] = static_cast<CRTP*>(this)->write_some(buffer);
std::cout << "bytes_consumed: " << bytes_consumed << "\n";
std::cout << "min_bytes_needed_next: " << min_bytes_needed_next << "\n";
buffer.remove_prefix(bytes_consumed);
}
while (bytes_consumed && (min_bytes_needed_next <= buffer.size()));
return buffer.empty();
}
class NewlineDelimitedPushDecoder
{
public:
};
class TransferEncodingChunkedPushDecoder: public UnbufferedPushDecoder<TransferEncodingChunkedPushDecoder>
{
public:
std::tuple<std::size_t, std::size_t>
write_some(std::string_view buffer);
private:
static constexpr std::size_t max_header_len_ = 256;
static constexpr std::size_t max_chunk_size_ = 0xFFFF;
};
std::tuple<std::size_t, std::size_t>
TransferEncodingChunkedPushDecoder::write_some(std::string_view buffer)
{
if (buffer.size() < 5) // "0\r\n\r\n"
{
return {0, 5 - buffer.size()};
}
auto chdr_crlf_pos = buffer.substr(0, std::min(max_header_len_, buffer.size()))
.find("\r\n");
if (chdr_crlf_pos == std::string_view::npos)
{
return {0, (buffer.size() < max_header_len_) ? 1 : 0};
}
std::size_t chunk_size = 0;
auto chdr_crlf_ptr = buffer.data() + chdr_crlf_pos;
auto chunk_size_conv = std::from_chars (buffer.data(), chdr_crlf_ptr, chunk_size, 16);
if (chunk_size_conv.ec != std::errc{})
{
std::cout << "bad chunk size\n";
return {0, 0};
}
if ((chunk_size_conv.ptr != chdr_crlf_ptr) && (*chunk_size_conv.ptr != ':'))
{
std::cout << "bad chunk header extensions\n";
return {0, 0};
}
//std::cout << "chunk size: " << chunk_size << "\n";
if (chunk_size > max_chunk_size_)
{
std::cout << "chunk exceeds maximum size\n";
return {0, 0};
}
auto frame_size = chdr_crlf_pos + 2 + chunk_size + 2;
//std::cout << "frame size: " << frame_size << "\n";
if (buffer.size() < frame_size)
{
return {0, frame_size - buffer.size()};
}
return {frame_size, buffer.size() - frame_size};
}
template <typename PushDecoder>
void
decode(PushDecoder& decoder, char const* ptr, size_t size)
{
std::string_view buffer(ptr, size);
decoder.write(buffer);
}
template <typename PushDecoder>
void
decode(PushDecoder& decoder, char const* ptr)
{
return decode(decoder, ptr, std::strlen(ptr));
}
int
main()
{
TransferEncodingChunkedPushDecoder chunk_decoder;
decode (chunk_decoder, "ffff\r\n6");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment