// // Dis VM // File: buffered_reader.h // Author: arr // #ifndef _DISVM_SRC_VM_BUFFERED_READER_H_ #define _DISVM_SRC_VM_BUFFERED_READER_H_ #include #include #include #include #include namespace disvm { namespace util { class buffered_reader_t { private: // static static const std::size_t buffer_size = 64 * 1024; // 64k is an optimization for the Windows filesystem manager public: buffered_reader_t(std::istream &stream) : _buffer(buffered_reader_t::buffer_size) , _current_index{ 0 } , _current_size{ 0 } , _stream{ stream } { assert(!stream.fail()); } buffered_reader_t(const buffered_reader_t&) = delete; buffered_reader_t& operator=(const buffered_reader_t&) = delete; // Get the next 'n' number of bytes. // Returns the number of bytes retrieved. uint32_t get_next_bytes(uint32_t bytes_requested, uint8_t *buffer) { if (bytes_requested == 0) return 0; assert(buffer != nullptr); auto bytes_requested_acc = uint32_t{0}; std::size_t amount_in_buffer; for (;;) { bool success = check_buffer_content(amount_in_buffer); if (!success || amount_in_buffer == 0) return 0; // Buffer contains requested amount if (bytes_requested <= amount_in_buffer) { std::memcpy(buffer, (_buffer.data() + _current_index), bytes_requested); _current_index += bytes_requested; bytes_requested_acc += bytes_requested; return bytes_requested_acc; } else { // Buffer does not contain all the data requested std::memcpy(buffer, (_buffer.data() + _current_index), amount_in_buffer); _current_index += amount_in_buffer; // Subtract the bytes consumed bytes_requested -= amount_in_buffer; bytes_requested_acc += amount_in_buffer; buffer += amount_in_buffer; } } } // Get the next byte in the buffer. // Returns success and the byte. std::tuple get_next_byte() { std::size_t amount_in_buffer; bool success = check_buffer_content(amount_in_buffer); if (!success || amount_in_buffer == 0) return std::make_tuple(false, 0); return std::make_tuple(true, _buffer[_current_index++]); } private: bool check_buffer_content(std::size_t &amount_in_buffer) { // Signed so checking for underflow is easier. amount_in_buffer = 0; auto amount_in_buffer_local = _current_size - _current_index; if (amount_in_buffer_local <= 0) { _current_index = 0; _current_size = 0; if (_stream.fail()) return false; _stream.read(_buffer.data(), buffered_reader_t::buffer_size); _current_size = _stream.fail() ? static_cast(_stream.gcount()) : buffered_reader_t::buffer_size; // Reset amount in buffer amount_in_buffer_local = buffered_reader_t::buffer_size; } amount_in_buffer = amount_in_buffer_local; return true; } private: std::istream &_stream; std::size_t _current_index; std::size_t _current_size; std::vector _buffer; }; } } #endif // _DISVM_SRC_VM_BUFFERED_READER_H_