ClickHouse/dbms/include/DB/IO/BufferBase.h
2017-01-02 23:08:23 +03:00

114 lines
5.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <DB/Core/Defines.h>
#include <algorithm>
namespace DB
{
/** Базовый класс для ReadBuffer и WriteBuffer.
* Содержит общие типы, переменные и функции.
*
* ReadBuffer и WriteBuffer похожи на istream и ostream, соответственно.
* Их приходится использовать, так как с использованием iostream-ов невозможно эффективно реализовать некоторые операции.
* Например, используя istream, невозможно быстро читать строковые значения из tab-separated файла,
* чтобы после чтения, позиция осталась сразу после считанного значения.
* (Единственный вариант - вызывать функцию std::istream::get() на каждый байт, но это тормозит из-за нескольких виртуальных вызовов.)
*
* Read/WriteBuffer-ы предоставляют прямой доступ к внутреннему буферу, поэтому, необходимые операции реализуются эффективнее.
* Используется только одна виртуальная функция nextImpl(), которая вызывается редко:
* - в случае ReadBuffer - заполнить буфер новыми данными из источика;
* - в случае WriteBuffer - записать данные из буфера в приёмник.
*
* Read/WriteBuffer-ы могут владеть или не владеть своим куском памяти.
* Во втором случае, можно эффективно читать из уже существующего куска памяти / std::string, не копируя его.
*/
class BufferBase
{
public:
/** Курсор в буфере. Позиция записи или чтения. */
using Position = char *;
/** Ссылка на диапазон памяти. */
struct Buffer
{
Buffer(Position begin_pos_, Position end_pos_) : begin_pos(begin_pos_), end_pos(end_pos_) {}
inline Position begin() const { return begin_pos; }
inline Position end() const { return end_pos; }
inline size_t size() const { return end_pos - begin_pos; }
inline void resize(size_t size) { end_pos = begin_pos + size; }
inline void swap(Buffer & other)
{
std::swap(begin_pos, other.begin_pos);
std::swap(end_pos, other.end_pos);
}
private:
Position begin_pos;
Position end_pos; /// на 1 байт после конца буфера
};
/** Конструктор принимает диапазон памяти, который следует использовать под буфер.
* offset - начальное место курсора. ReadBuffer должен установить его в конец диапазона, а WriteBuffer - в начало.
*/
BufferBase(Position ptr, size_t size, size_t offset)
: internal_buffer(ptr, ptr + size), working_buffer(ptr, ptr + size), pos(ptr + offset) {}
void set(Position ptr, size_t size, size_t offset)
{
internal_buffer = Buffer(ptr, ptr + size);
working_buffer = Buffer(ptr, ptr + size);
pos = ptr + offset;
}
/// получить буфер
inline Buffer & internalBuffer() { return internal_buffer; }
/// получить часть буфера, из которого можно читать / в который можно писать данные
inline Buffer & buffer() { return working_buffer; }
/// получить (для чтения и изменения) позицию в буфере
inline Position & position() { return pos; };
/// смещение в байтах курсора от начала буфера
inline size_t offset() const { return pos - working_buffer.begin(); }
/** Сколько байт было прочитано/записано, считая те, что ещё в буфере. */
size_t count() const
{
return bytes + offset();
}
/** Проверить, есть ли данные в буфере. */
bool ALWAYS_INLINE hasPendingData() const
{
return pos != working_buffer.end();
}
protected:
/// Ссылка на кусок памяти для буфера.
Buffer internal_buffer;
/** Часть куска памяти, которую можно использовать.
* Например, если internal_buffer - 1MB, а из файла для чтения было загружено в буфер
* только 10 байт, то working_buffer будет иметь размер 10 байт
* (working_buffer.end() будет указывать на позицию сразу после тех 10 байт, которых можно прочитать).
*/
Buffer working_buffer;
/// Позиция чтения/записи.
Position pos;
/** Сколько байт было прочитано/записано, не считая тех, что сейчас в буфере.
* (считая те, что были уже использованы и "удалены" из буфера)
*/
size_t bytes = 0;
};
}