#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
class Parser {
    int remaining=-1; std::string current;
public:
    std::vector<std::string> frames;
    void feed(const std::vector<std::uint8_t>& bytes) {
        for(auto b:bytes) {
            if(remaining<0) {
                if(b>8 || frames.size()>=16) throw std::runtime_error("limit");
                remaining=b; current.clear();
                if(remaining==0) {frames.emplace_back(); remaining=-1;}
            } else {
                current.push_back(static_cast<char>(b));
                if(--remaining==0) {frames.push_back(current); remaining=-1;}
            }
        }
    }
    void finish() const {if(remaining>=0) throw std::runtime_error("truncated");}
};
int main() {
    const std::vector<std::uint8_t> wire{3,'c','a','t',2,'h','i'};
    Parser one,chunk; for(auto b:wire) one.feed({b}); chunk.feed(wire);
    one.finish(); chunk.finish();
    if(one.frames!=chunk.frames || one.frames!=std::vector<std::string>{"cat","hi"})
        throw std::runtime_error("framing");
    int invalid=0;
    for(const auto& bytes:std::vector<std::vector<std::uint8_t>>{{9},{3,'x'}}) {
        try {Parser p;p.feed(bytes);p.finish();} catch(const std::runtime_error&) {++invalid;}
    }
    Parser empty;empty.feed({0});empty.finish();
    if(invalid!=2 || empty.frames!=std::vector<std::string>{""}) throw std::runtime_error("edges");
    std::cout<<"frames=cat,hi invalid="<<invalid<<" empty="<<empty.frames.size()<<'\n';
}
