blob: 239e82d950a7a546beedf3e0f41fc2c5f34d3e42 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// iobuf - A non-continuous zero-copied buffer
// Date: Thu Nov 22 13:57:56 CST 2012
#ifndef BUTIL_IOBUF_H
#define BUTIL_IOBUF_H
#include <sys/uio.h> // iovec
#include <stdint.h> // uint32_t, int64_t
#include <functional>
#include <string> // std::string
#include <ostream> // std::ostream
#include <google/protobuf/io/zero_copy_stream.h> // ZeroCopyInputStream
#include "butil/strings/string_piece.h" // butil::StringPiece
#include "butil/third_party/snappy/snappy-sinksource.h"
#include "butil/zero_copy_stream_as_streambuf.h"
#include "butil/macros.h"
#include "butil/reader_writer.h"
#include "butil/binary_printer.h"
// For IOBuf::appendv(const const_iovec*, size_t). The only difference of this
// struct from iovec (defined in sys/uio.h) is that iov_base is `const void*'
// which is assignable by const pointers w/o any error.
extern "C" {
struct const_iovec {
const void* iov_base;
size_t iov_len;
};
#ifndef USE_MESALINK
struct ssl_st;
#else
#define ssl_st MESALINK_SSL
#endif
}
namespace butil {
// IOBuf is a non-continuous buffer that can be cut and combined w/o copying
// payload. It can be read from or flushed into file descriptors as well.
// IOBuf is [thread-compatible]. Namely using different IOBuf in different
// threads simultaneously is safe, and reading a static IOBuf from different
// threads is safe as well.
// IOBuf is [NOT thread-safe]. Modifying a same IOBuf from different threads
// simultaneously is unsafe and likely to crash.
class IOBuf {
friend class IOBufAsZeroCopyInputStream;
friend class IOBufAsZeroCopyOutputStream;
friend class IOBufBytesIterator;
friend class IOBufCutter;
friend class SingleIOBuf;
public:
static const size_t DEFAULT_BLOCK_SIZE = 8192;
static const size_t INITIAL_CAP = 32; // must be power of 2
struct Block;
// can't directly use `struct iovec' here because we also need to access the
// reference counter(nshared) in Block*
struct BlockRef {
// NOTICE: first bit of `offset' is shared with BigView::start
uint32_t offset;
uint32_t length;
Block* block;
};
// IOBuf is essentially a tiny queue of BlockRefs.
struct SmallView {
BlockRef refs[2];
};
struct BigView {
int32_t magic;
uint32_t start;
BlockRef* refs;
uint32_t nref;
uint32_t cap_mask;
size_t nbytes;
const BlockRef& ref_at(uint32_t i) const
{ return refs[(start + i) & cap_mask]; }
BlockRef& ref_at(uint32_t i)
{ return refs[(start + i) & cap_mask]; }
uint32_t capacity() const { return cap_mask + 1; }
};
struct Movable {
explicit Movable(IOBuf& v) : _v(&v) { }
IOBuf& value() const { return *_v; }
private:
IOBuf *_v;
};
typedef uint64_t Area;
static const Area INVALID_AREA = 0;
IOBuf();
IOBuf(const IOBuf&);
IOBuf(const Movable&);
~IOBuf() { clear(); }
void operator=(const IOBuf&);
void operator=(const Movable&);
void operator=(const char*);
void operator=(const std::string&);
// Exchange internal fields with another IOBuf.
void swap(IOBuf&);
// Pop n bytes from front side
// If n == 0, nothing popped; if n >= length(), all bytes are popped
// Returns bytes popped.
size_t pop_front(size_t n);
// Pop n bytes from back side
// If n == 0, nothing popped; if n >= length(), all bytes are popped
// Returns bytes popped.
size_t pop_back(size_t n);
// Cut off n bytes from front side and APPEND to `out'
// If n == 0, nothing cut; if n >= length(), all bytes are cut
// Returns bytes cut.
size_t cutn(IOBuf* out, size_t n);
size_t cutn(void* out, size_t n);
size_t cutn(std::string* out, size_t n);
// Cut off 1 byte from the front side and set to *c
// Return true on cut, false otherwise.
bool cut1(void* c);
// Cut from front side until the characters matches `delim', append
// data before the matched characters to `out'.
// Returns 0 on success, -1 when there's no match (including empty `delim')
// or other errors.
int cut_until(IOBuf* out, char const* delim);
// std::string version, `delim' could be binary
int cut_until(IOBuf* out, const std::string& delim);
// Cut at most `size_hint' bytes(approximately) into the writer
// Returns bytes cut on success, -1 otherwise and errno is set.
ssize_t cut_into_writer(IWriter* writer, size_t size_hint = 1024*1024);
// Cut at most `size_hint' bytes(approximately) into the file descriptor
// Returns bytes cut on success, -1 otherwise and errno is set.
ssize_t cut_into_file_descriptor(int fd, size_t size_hint = 1024*1024);
// Cut at most `size_hint' bytes(approximately) into the file descriptor at
// a given offset(from the start of the file). The file offset is not changed.
// If `offset' is negative, does exactly what cut_into_file_descriptor does.
// Returns bytes cut on success, -1 otherwise and errno is set.
//
// NOTE: POSIX requires that a file open with the O_APPEND flag should
// not affect pwrite(). However, on Linux, if |fd| is open with O_APPEND,
// pwrite() appends data to the end of the file, regardless of the value
// of |offset|.
ssize_t pcut_into_file_descriptor(int fd, off_t offset /*NOTE*/,
size_t size_hint = 1024*1024);
// Cut into SSL channel `ssl'. Returns what `SSL_write' returns
// and the ssl error code will be filled into `ssl_error'
ssize_t cut_into_SSL_channel(struct ssl_st* ssl, int* ssl_error);
// Cut `count' number of `pieces' into the writer.
// Returns bytes cut on success, -1 otherwise and errno is set.
static ssize_t cut_multiple_into_writer(
IWriter* writer, IOBuf* const* pieces, size_t count);
// Cut `count' number of `pieces' into the file descriptor.
// Returns bytes cut on success, -1 otherwise and errno is set.
static ssize_t cut_multiple_into_file_descriptor(
int fd, IOBuf* const* pieces, size_t count);
// Cut `count' number of `pieces' into file descriptor `fd' at a given
// offset. The file offset is not changed.
// If `offset' is negative, does exactly what cut_multiple_into_file_descriptor
// does.
// Read NOTE of pcut_into_file_descriptor.
// Returns bytes cut on success, -1 otherwise and errno is set.
static ssize_t pcut_multiple_into_file_descriptor(
int fd, off_t offset, IOBuf* const* pieces, size_t count);
// Cut `count' number of `pieces' into SSL channel `ssl'.
// Returns bytes cut on success, -1 otherwise and errno is set.
static ssize_t cut_multiple_into_SSL_channel(
struct ssl_st* ssl, IOBuf* const* pieces, size_t count, int* ssl_error);
// Append another IOBuf to back side, payload of the IOBuf is shared
// rather than copied.
void append(const IOBuf& other);
// Append content of `other' to self and clear `other'.
void append(const Movable& other);
// ===================================================================
// Following push_back()/append() are just implemented for convenience
// and occasional usages, they're relatively slow because of the overhead
// of frequent BlockRef-management and reference-countings. If you get
// a lot of push_back/append to do, you should use IOBufAppender or
// IOBufBuilder instead, which reduce overhead by owning IOBuf::Block.
// ===================================================================
// Append a character to back side. (with copying)
// Returns 0 on success, -1 otherwise.
int push_back(char c);
// Append `data' with `count' bytes to back side. (with copying)
// Returns 0 on success(include count == 0), -1 otherwise.
int append(void const* data, size_t count);
// Append multiple data to back side in one call, faster than appending
// one by one separately.
// Returns 0 on success, -1 otherwise.
// Example:
// const_iovec vec[] = { { data1, len1 },
// { data2, len2 },
// { data3, len3 } };
// foo.appendv(vec, arraysize(vec));
int appendv(const const_iovec vec[], size_t n);
int appendv(const iovec* vec, size_t n)
{ return appendv((const const_iovec*)vec, n); }
// Append a c-style string to back side. (with copying)
// Returns 0 on success, -1 otherwise.
// NOTE: Returns 0 when `s' is empty.
int append(char const* s);
// Append a std::string to back side. (with copying)
// Returns 0 on success, -1 otherwise.
// NOTE: Returns 0 when `s' is empty.
int append(const std::string& s);
// Append the user-data to back side WITHOUT copying.
// The user-data can be split and shared by smaller IOBufs and will be
// deleted using the deleter func when no IOBuf references it anymore.
int append_user_data(void* data, size_t size, std::function<void(void*)> deleter);
// Append the user-data to back side WITHOUT copying.
// The meta is associated with this piece of user-data.
int append_user_data_with_meta(void* data, size_t size, std::function<void(void*)> deleter, uint64_t meta);
// Get the data meta of the first byte in this IOBuf.
// The meta is specified with append_user_data_with_meta before.
// 0 means the meta is invalid.
uint64_t get_first_data_meta();
// Resizes the buf to a length of n characters.
// If n is smaller than the current length, all bytes after n will be
// truncated.
// If n is greater than the current length, the buffer would be append with
// as many |c| as needed to reach a size of n. If c is not specified,
// null-character would be appended.
// Returns 0 on success, -1 otherwise.
int resize(size_t n) { return resize(n, '\0'); }
int resize(size_t n, char c);
// Reserve `n' uninitialized bytes at back-side.
// Returns an object representing the reserved area, INVALID_AREA on failure.
// NOTE: reserve(0) returns INVALID_AREA.
Area reserve(size_t n);
// [EXTREMELY UNSAFE]
// Copy `data' to the reserved `area'. `data' must be as long as the
// reserved size.
// Returns 0 on success, -1 otherwise.
// [Rules]
// 1. Make sure the IOBuf to be assigned was NOT cut/pop from front side
// after reserving, otherwise behavior of this function is undefined,
// even if it returns 0.
// 2. Make sure the IOBuf to be assigned was NOT copied to/from another
// IOBuf after reserving to prevent underlying blocks from being shared,
// otherwise the assignment affects all IOBuf sharing the blocks, which
// is probably not what we want.
int unsafe_assign(Area area, const void* data);
// Append min(n, length()) bytes starting from `pos' at front side to `buf'.
// The real payload is shared rather than copied.
// Returns bytes copied.
size_t append_to(IOBuf* buf, size_t n = (size_t)-1L, size_t pos = 0) const;
// Explicitly declare this overload as error to avoid copy_to(butil::IOBuf*)
// from being interpreted as copy_to(void*) by the compiler (which causes
// undefined behavior).
size_t copy_to(IOBuf* buf, size_t n = (size_t)-1L, size_t pos = 0) const
// the error attribute in not available in gcc 3.4
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
__attribute__ (( error("Call append_to(IOBuf*) instead") ))
#endif
;
// Copy min(n, length()) bytes starting from `pos' at front side into `buf'.
// Returns bytes copied.
size_t copy_to(void* buf, size_t n = (size_t)-1L, size_t pos = 0) const;
// NOTE: first parameter is not std::string& because user may pass in
// a pointer of std::string by mistake, in which case, the void* overload
// would be wrongly called.
size_t copy_to(std::string* s, size_t n = (size_t)-1L, size_t pos = 0) const;
size_t append_to(std::string* s, size_t n = (size_t)-1L, size_t pos = 0) const;
// Copy min(n, length()) bytes staring from `pos' at front side into
// `cstr' and end it with '\0'.
// `cstr' must be as long as min(n, length())+1.
// Returns bytes copied (not including ending '\0')
size_t copy_to_cstr(char* cstr, size_t n = (size_t)-1L, size_t pos = 0) const;
// Convert all data in this buffer to a std::string.
std::string to_string() const;
// Get `n' front-side bytes with minimum copying. Length of `aux_buffer'
// must not be less than `n'.
// Returns:
// NULL - n is greater than length()
// aux_buffer - n bytes are copied into aux_buffer
// internal buffer - the bytes are stored continuously in the internal
// buffer, no copying is needed. This function does not
// add additional reference to the underlying block,
// so user should not change this IOBuf during using
// the internal buffer.
// If n == 0 and buffer is empty, return value is undefined.
const void* fetch(void* aux_buffer, size_t n) const;
// Fetch one character from front side.
// Returns pointer to the character, NULL on empty.
const void* fetch1() const;
// Remove all data
void clear();
// True iff there's no data
bool empty() const;
// Number of bytes
size_t length() const;
size_t size() const { return length(); }
// Get number of Blocks in use. block_memory = block_count * BLOCK_SIZE
static size_t block_count();
static size_t block_memory();
static size_t new_bigview_count();
static size_t block_count_hit_tls_threshold();
// Equal with a string/IOBuf or not.
bool equals(const butil::StringPiece&) const;
bool equals(const IOBuf& other) const;
// Get the number of backing blocks
size_t backing_block_num() const { return _ref_num(); }
// Get #i backing_block, an empty StringPiece is returned if no such block
StringPiece backing_block(size_t i) const;
// Make a movable version of self
Movable movable() { return Movable(*this); }
protected:
int _cut_by_char(IOBuf* out, char);
int _cut_by_delim(IOBuf* out, char const* dbegin, size_t ndelim);
// Returns: true iff this should be viewed as SmallView
bool _small() const;
template <bool MOVE>
void _push_or_move_back_ref_to_smallview(const BlockRef&);
template <bool MOVE>
void _push_or_move_back_ref_to_bigview(const BlockRef&);
// Push a BlockRef to back side
// NOTICE: All fields of the ref must be initialized or assigned
// properly, or it will ruin this queue
void _push_back_ref(const BlockRef&);
// Move a BlockRef to back side. After calling this function, content of
// the BlockRef will be invalid and should never be used again.
void _move_back_ref(const BlockRef&);
// Pop a BlockRef from front side.
// Returns: 0 on success and -1 on empty.
int _pop_front_ref() { return _pop_or_moveout_front_ref<false>(); }
// Move a BlockRef out from front side.
// Returns: 0 on success and -1 on empty.
int _moveout_front_ref() { return _pop_or_moveout_front_ref<true>(); }
template <bool MOVEOUT>
int _pop_or_moveout_front_ref();
// Pop a BlockRef from back side.
// Returns: 0 on success and -1 on empty.
int _pop_back_ref();
// Number of refs in the queue
size_t _ref_num() const;
// Get reference to front/back BlockRef in the queue
// should not be called if queue is empty or the behavior is undefined
BlockRef& _front_ref();
const BlockRef& _front_ref() const;
BlockRef& _back_ref();
const BlockRef& _back_ref() const;
// Get reference to n-th BlockRef(counting from front) in the queue
// NOTICE: should not be called if queue is empty and the `n' must
// be inside [0, _ref_num()-1] or behavior is undefined
BlockRef& _ref_at(size_t i);
const BlockRef& _ref_at(size_t i) const;
// Get pointer to n-th BlockRef(counting from front)
// If i is out-of-range, NULL is returned.
const BlockRef* _pref_at(size_t i) const;
private:
union {
BigView _bv;
SmallView _sv;
};
};
std::ostream& operator<<(std::ostream&, const IOBuf& buf);
inline bool operator==(const butil::IOBuf& b, const butil::StringPiece& s)
{ return b.equals(s); }
inline bool operator==(const butil::StringPiece& s, const butil::IOBuf& b)
{ return b.equals(s); }
inline bool operator!=(const butil::IOBuf& b, const butil::StringPiece& s)
{ return !b.equals(s); }
inline bool operator!=(const butil::StringPiece& s, const butil::IOBuf& b)
{ return !b.equals(s); }
inline bool operator==(const butil::IOBuf& b1, const butil::IOBuf& b2)
{ return b1.equals(b2); }
inline bool operator!=(const butil::IOBuf& b1, const butil::IOBuf& b2)
{ return !b1.equals(b2); }
// IOPortal is a subclass of IOBuf that can read from file descriptors.
// Typically used as the buffer to store bytes from sockets.
class IOPortal : public IOBuf {
public:
IOPortal() : _block(NULL) { }
IOPortal(const IOPortal& rhs) : IOBuf(rhs), _block(NULL) { }
~IOPortal();
IOPortal& operator=(const IOPortal& rhs);
// Read at most `max_count' bytes from the reader and append to self.
ssize_t append_from_reader(IReader* reader, size_t max_count);
// Read at most `max_count' bytes from file descriptor `fd' and
// append to self.
ssize_t append_from_file_descriptor(int fd, size_t max_count);
// Read at most `max_count' bytes from file descriptor `fd' at a given
// offset and append to self. The file offset is not changed.
// If `offset' is negative, does exactly what append_from_file_descriptor does.
ssize_t pappend_from_file_descriptor(int fd, off_t offset, size_t max_count);
// Read as many bytes as possible from SSL channel `ssl', and stop until `max_count'.
// Returns total bytes read and the ssl error code will be filled into `ssl_error'
ssize_t append_from_SSL_channel(struct ssl_st* ssl, int* ssl_error,
size_t max_count = 1024*1024);
// Remove all data inside and return cached blocks.
void clear();
// Return cached blocks to TLS. This function should be called by users
// when this IOPortal are cut into intact messages and becomes empty, to
// let continuing code on IOBuf to reuse the blocks. Calling this function
// after each call to append_xxx does not make sense and may hurt
// performance. Read comments on field `_block' below.
void return_cached_blocks();
private:
static void return_cached_blocks_impl(Block*);
// Cached blocks for appending. Notice that the blocks are released
// until return_cached_blocks()/clear()/dtor() are called, rather than
// released after each append_xxx(), which makes messages read from one
// file descriptor more likely to share blocks and have less BlockRefs.
Block* _block;
};
class IOReserveAlignedBuf : public IOBuf {
public:
IOReserveAlignedBuf(size_t alignment)
: _alignment(alignment), _reserved(false) {}
Area reserve(size_t count);
private:
size_t _alignment;
bool _reserved;
};
// Specialized utility to cut from IOBuf faster than using corresponding
// methods in IOBuf.
// Designed for efficiently parsing data from IOBuf.
// The cut IOBuf can be appended during cutting.
class IOBufCutter {
public:
explicit IOBufCutter(butil::IOBuf* buf);
~IOBufCutter();
// Cut off n bytes and APPEND to `out'
// Returns bytes cut.
size_t cutn(butil::IOBuf* out, size_t n);
size_t cutn(std::string* out, size_t n);
size_t cutn(void* out, size_t n);
// Cut off 1 byte from the front side and set to *c
// Return true on cut, false otherwise.
bool cut1(void* data);
// Copy n bytes into `data'
// Returns bytes copied.
size_t copy_to(void* data, size_t n);
// Fetch one character.
// Returns pointer to the character, NULL on empty
const void* fetch1();
// Pop n bytes from front side
// Returns bytes popped.
size_t pop_front(size_t n);
// Uncut bytes
size_t remaining_bytes() const;
private:
size_t slower_copy_to(void* data, size_t n);
bool load_next_ref();
private:
void* _data;
void* _data_end;
IOBuf::Block* _block;
IOBuf* _buf;
};
// Parse protobuf message from IOBuf. Notice that this wrapper does not change
// source IOBuf, which also should not change during lifetime of the wrapper.
// Even if a IOBufAsZeroCopyInputStream is created but parsed, the source
// IOBuf should not be changed as well becuase constructor of the stream
// saves internal information of the source IOBuf which is assumed to be
// unchanged.
// Example:
// IOBufAsZeroCopyInputStream wrapper(the_iobuf_with_protobuf_format_data);
// some_pb_message.ParseFromZeroCopyStream(&wrapper);
class IOBufAsZeroCopyInputStream
: public google::protobuf::io::ZeroCopyInputStream {
public:
explicit IOBufAsZeroCopyInputStream(const IOBuf&);
bool Next(const void** data, int* size) override;
void BackUp(int count) override;
bool Skip(int count) override;
int64_t ByteCount() const override;
private:
int _ref_index;
int _add_offset;
int64_t _byte_count;
const IOBuf* _buf;
};
// Serialize protobuf message into IOBuf. This wrapper does not clear source
// IOBuf before appending. You can change the source IOBuf when stream is
// not used(append sth. to the IOBuf, serialize a protobuf message, append
// sth. again, serialize messages again...). This is different from
// IOBufAsZeroCopyInputStream which needs the source IOBuf to be unchanged.
// Example:
// IOBufAsZeroCopyOutputStream wrapper(&the_iobuf_to_put_data_in);
// some_pb_message.SerializeToZeroCopyStream(&wrapper);
//
// NOTE: Blocks are by default shared among all the ZeroCopyOutputStream in one
// thread. If there are many manipulated streams at one time, there may be many
// fragments. You can create a ZeroCopyOutputStream which has its own block by
// passing a positive `block_size' argument to avoid this problem.
class IOBufAsZeroCopyOutputStream
: public google::protobuf::io::ZeroCopyOutputStream {
public:
explicit IOBufAsZeroCopyOutputStream(IOBuf*);
IOBufAsZeroCopyOutputStream(IOBuf*, uint32_t block_size);
~IOBufAsZeroCopyOutputStream();
bool Next(void** data, int* size) override;
void BackUp(int count) override; // `count' can be as long as ByteCount()
int64_t ByteCount() const override;
private:
void _release_block();
IOBuf* _buf;
uint32_t _block_size;
IOBuf::Block *_cur_block;
int64_t _byte_count;
};
// Wrap IOBuf into input of snappy compression.
class IOBufAsSnappySource : public butil::snappy::Source {
public:
explicit IOBufAsSnappySource(const butil::IOBuf& buf)
: _buf(&buf), _stream(buf) {}
virtual ~IOBufAsSnappySource() {}
// Return the number of bytes left to read from the source
size_t Available() const override;
// Peek at the next flat region of the source.
const char* Peek(size_t* len) override;
// Skip the next n bytes. Invalidates any buffer returned by
// a previous call to Peek().
void Skip(size_t n) override;
private:
const butil::IOBuf* _buf;
butil::IOBufAsZeroCopyInputStream _stream;
};
// Wrap IOBuf into output of snappy compression.
class IOBufAsSnappySink : public butil::snappy::Sink {
public:
explicit IOBufAsSnappySink(butil::IOBuf& buf);
virtual ~IOBufAsSnappySink() {}
// Append "bytes[0,n-1]" to this.
void Append(const char* bytes, size_t n) override;
// Returns a writable buffer of the specified length for appending.
char* GetAppendBuffer(size_t length, char* scratch) override;
private:
char* _cur_buf;
int _cur_len;
butil::IOBuf* _buf;
butil::IOBufAsZeroCopyOutputStream _buf_stream;
};
// A std::ostream to build IOBuf.
// Example:
// IOBufBuilder builder;
// builder << "Anything that can be sent to std::ostream";
// // You have several methods to fetch the IOBuf.
// target_iobuf.append(builder.buf()); // builder.buf() was not changed
// OR
// builder.move_to(target_iobuf); // builder.buf() was clear()-ed.
class IOBufBuilder :
// Have to use private inheritance to arrange initialization order.
virtual private IOBuf,
virtual private IOBufAsZeroCopyOutputStream,
virtual private ZeroCopyStreamAsStreamBuf,
public std::ostream {
public:
explicit IOBufBuilder()
: IOBufAsZeroCopyOutputStream(this)
, ZeroCopyStreamAsStreamBuf(this)
, std::ostream(this)
{ }
IOBuf& buf() {
this->shrink();
return *this;
}
void buf(const IOBuf& buf) {
*static_cast<IOBuf*>(this) = buf;
}
void move_to(IOBuf& target) {
target = Movable(buf());
}
};
// Create IOBuf by appending data *faster*
class IOBufAppender {
public:
IOBufAppender();
// Append `n' bytes starting from `data' to back side of the internal buffer
// Costs 2/3 time of IOBuf.append for short data/strings on Intel(R) Xeon(R)
// CPU E5-2620 @ 2.00GHz. Longer data/strings make differences smaller.
// Returns 0 on success, -1 otherwise.
int append(const void* data, size_t n);
int append(const butil::StringPiece& str);
// Format integer |d| to back side of the internal buffer, which is much faster
// than snprintf(..., "%lu", d).
// Returns 0 on success, -1 otherwise.
int append_decimal(long d);
// Push the character to back side of the internal buffer.
// Costs ~3ns while IOBuf.push_back costs ~13ns on Intel(R) Xeon(R) CPU
// E5-2620 @ 2.00GHz
// Returns 0 on success, -1 otherwise.
int push_back(char c);
IOBuf& buf() {
shrink();
return _buf;
}
void move_to(IOBuf& target) {
target = IOBuf::Movable(buf());
}
private:
void shrink();
int add_block();
void* _data;
// Saving _data_end instead of _size avoid modifying _data and _size
// in each push_back() which is probably a hotspot.
void* _data_end;
IOBuf _buf;
IOBufAsZeroCopyOutputStream _zc_stream;
};
// Iterate bytes of a IOBuf.
// During iteration, the iobuf should NOT be changed.
class IOBufBytesIterator {
public:
explicit IOBufBytesIterator(const butil::IOBuf& buf);
// Construct from another iterator.
IOBufBytesIterator(const IOBufBytesIterator& it);
IOBufBytesIterator(const IOBufBytesIterator& it, size_t bytes_left);
// Returning unsigned is safer than char which would be more error prone
// to bitwise operations. For example: in "uint32_t value = *it", value
// is (unexpected) 4294967168 when *it returns (char)128.
unsigned char operator*() const { return (unsigned char)*_block_begin; }
operator const void*() const { return (const void*)!!_bytes_left; }
void operator++();
void operator++(int) { return operator++(); }
// Copy at most n bytes into buf, forwarding this iterator.
// Returns bytes copied.
size_t copy_and_forward(void* buf, size_t n);
size_t copy_and_forward(std::string* s, size_t n);
// Just forward this iterator for at most n bytes.
size_t forward(size_t n);
// Append at most n bytes into buf, forwarding this iterator. Data are
// referenced rather than copied.
size_t append_and_forward(butil::IOBuf* buf, size_t n);
bool forward_one_block(const void** data, size_t* size);
size_t bytes_left() const { return _bytes_left; }
private:
void try_next_block();
const char* _block_begin;
const char* _block_end;
uint32_t _block_count;
uint32_t _bytes_left;
const butil::IOBuf* _buf;
};
} // namespace butil
// Specialize std::swap for IOBuf
#if __cplusplus < 201103L // < C++11
#include <algorithm> // std::swap until C++11
#else
#include <utility> // std::swap since C++11
#endif // __cplusplus < 201103L
namespace std {
template <>
inline void swap(butil::IOBuf& a, butil::IOBuf& b) {
return a.swap(b);
}
} // namespace std
#include "butil/iobuf_inl.h"
#endif // BUTIL_IOBUF_H