#include <cstdint>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <memory>

class BufferedReader {
 public:
    explicit BufferedReader(PackageStream* stream) : stream(stream),
        buffer(new char[stream->PackageLen() * 2]), d(0) {
    }

    ~BufferedReader() {
        delete[] buffer;
    }

    int32_t Read(char* output_buffer, int32_t buffer_len) {
        int32_t pos = 0;

        if (d > 0) {
            memcpy(output_buffer, buffer, std::min(d, buffer_len));
            pos = std::min(d, buffer_len);
            d -= pos;

            memmove(buffer, buffer + pos, d);
        }

        if (pos >= buffer_len)
            return pos;

        int32_t new_len = stream->ReadPackage(buffer);
        int32_t cur_len = 0;

        while (new_len > 0 && pos < buffer_len) {
            cur_len = new_len;
            memcpy(output_buffer + pos, buffer,
                   std::min(buffer_len - pos, cur_len));
            d = cur_len - std::min(cur_len, buffer_len - pos);
            pos += std::min(buffer_len - pos, cur_len);

            if (pos < buffer_len)
                new_len = stream->ReadPackage(buffer);
        }

        if (d > 0) {
            memmove(buffer, buffer + (cur_len - d), d);
        }

        return pos;
    }

 private:
    PackageStream* stream;
    char* buffer;
    int32_t d;
};