blob: 646a94b4c167a9a2b5dea1dc5ad6012d55ccd706 [file]
/*
* 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.
*/
#pragma once
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include "fury/util/bit_util.h"
#include "fury/util/logging.h"
namespace fury {
// A buffer class for storing raw bytes with various methods for reading and
// writing the bytes.
class Buffer {
public:
Buffer();
Buffer(uint8_t *data, uint32_t size, bool own_data = true)
: data_(data), size_(size), own_data_(own_data) {
writer_index_ = 0;
reader_index_ = 0;
}
Buffer(Buffer &&buffer) noexcept;
Buffer &operator=(Buffer &&buffer) noexcept;
virtual ~Buffer();
/// \brief Return a pointer to the buffer's data
inline uint8_t *data() const { return data_; }
/// \brief Return the buffer's size in bytes
inline uint32_t size() const { return size_; }
inline bool own_data() const { return own_data_; }
inline uint32_t writer_index() { return writer_index_; }
inline uint32_t reader_index() { return reader_index_; }
inline void WriterIndex(uint32_t writer_index) {
FURY_CHECK(writer_index < std::numeric_limits<int>::max())
<< "Buffer overflow writer_index" << writer_index_
<< " target writer_index " << writer_index;
writer_index_ = writer_index;
}
inline void IncreaseWriterIndex(uint32_t diff) {
int64_t writer_index = writer_index_ + diff;
FURY_CHECK(writer_index < std::numeric_limits<int>::max())
<< "Buffer overflow writer_index" << writer_index_ << " diff " << diff;
writer_index_ = writer_index;
}
inline void ReaderIndex(uint32_t reader_index) {
FURY_CHECK(reader_index < std::numeric_limits<int>::max())
<< "Buffer overflow reader_index" << reader_index_
<< " target reader_index " << reader_index;
reader_index_ = reader_index;
}
inline void IncreaseReaderIndex(uint32_t diff) {
int64_t reader_index = reader_index_ + diff;
FURY_CHECK(reader_index < std::numeric_limits<int>::max())
<< "Buffer overflow reader_index" << reader_index_ << " diff " << diff;
reader_index_ = reader_index;
}
// Unsafe methods don't check bound
template <typename T> inline void UnsafePut(uint32_t offset, T value) {
reinterpret_cast<T *>(data_ + offset)[0] = value;
}
template <typename T,
typename = meta::EnableIfIsOneOf<T, int8_t, uint8_t, bool>>
inline void UnsafePutByte(uint32_t offset, T value) {
data_[offset] = value;
}
inline void UnsafePut(uint32_t offset, const void *data,
const uint32_t length) {
memcpy(data_ + offset, data, (size_t)length);
}
template <typename T> inline T Get(uint32_t relative_offset) {
FURY_CHECK(relative_offset < size_) << "Out of range " << relative_offset
<< " should be less than " << size_;
T value = reinterpret_cast<const T *>(data_ + relative_offset)[0];
return value;
}
template <typename T,
typename = meta::EnableIfIsOneOf<T, int8_t, uint8_t, bool>>
inline T GetByteAs(uint32_t relative_offset) {
FURY_CHECK(relative_offset < size_) << "Out of range " << relative_offset
<< " should be less than " << size_;
return data_[relative_offset];
}
inline bool GetBool(uint32_t offset) { return GetByteAs<bool>(offset); }
inline int8_t GetInt8(uint32_t offset) { return GetByteAs<int8_t>(offset); }
inline int16_t GetInt16(uint32_t offset) { return Get<int16_t>(offset); }
inline int32_t GetInt32(uint32_t offset) { return Get<int32_t>(offset); }
inline int64_t GetInt64(uint32_t offset) { return Get<int64_t>(offset); }
inline float GetFloat(uint32_t offset) { return Get<float>(offset); }
inline double GetDouble(uint32_t offset) { return Get<double>(offset); }
inline uint32_t PutPositiveVarInt32(uint32_t offset, int32_t value) {
if (value >> 7 == 0) {
data_[offset] = (int8_t)value;
return 1;
}
if (value >> 14 == 0) {
data_[offset++] = (int8_t)((value & 0x7F) | 0x80);
data_[offset++] = (int8_t)(value >> 7);
return 2;
}
if (value >> 21 == 0) {
data_[offset++] = (int8_t)((value & 0x7F) | 0x80);
data_[offset++] = (int8_t)(value >> 7 | 0x80);
data_[offset++] = (int8_t)(value >> 14);
return 3;
}
if (value >> 28 == 0) {
data_[offset++] = (int8_t)((value & 0x7F) | 0x80);
data_[offset++] = (int8_t)(value >> 7 | 0x80);
data_[offset++] = (int8_t)(value >> 14 | 0x80);
data_[offset++] = (int8_t)(value >> 21);
return 4;
}
data_[offset++] = (int8_t)((value & 0x7F) | 0x80);
data_[offset++] = (int8_t)(value >> 7 | 0x80);
data_[offset++] = (int8_t)(value >> 14 | 0x80);
data_[offset++] = (int8_t)(value >> 21 | 0x80);
data_[offset++] = (int8_t)(value >> 28);
return 5;
}
inline int32_t GetPositiveVarInt32(uint32_t offset,
uint32_t *readBytesLength) {
uint32_t position = offset;
int b = data_[position++];
int result = b & 0x7F;
if ((b & 0x80) != 0) {
b = data_[position++];
result |= (b & 0x7F) << 7;
if ((b & 0x80) != 0) {
b = data_[position++];
result |= (b & 0x7F) << 14;
if ((b & 0x80) != 0) {
b = data_[position++];
result |= (b & 0x7F) << 21;
if ((b & 0x80) != 0) {
b = data_[position++];
result |= (b & 0x7F) << 28;
}
}
}
}
*readBytesLength = position - offset;
return result;
}
/// Return true if both buffers are the same size and contain the same bytes
/// up to the number of compared bytes
bool Equals(const Buffer &other, int64_t nbytes) const;
/// Return true if both buffers are the same size and contain the same bytes
bool Equals(const Buffer &other) const;
inline void Grow(uint32_t min_capacity) {
uint32_t len = writer_index_ + min_capacity;
if (len > size_) {
// NOTE: over allocate by 1.5 or 2 ?
// see: Doubling isn't a great overallocation practice
// see
// https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md
// for discussion.
auto new_size = util::RoundNumberOfBytesToNearestWord(len * 2);
Reserve(new_size);
}
}
/// Reserve buffer to new_size
void Reserve(uint32_t new_size) {
if (new_size > size_) {
uint8_t *new_ptr;
if (own_data_) {
new_ptr = static_cast<uint8_t *>(
realloc(data_, static_cast<size_t>(new_size)));
} else {
new_ptr = static_cast<uint8_t *>(malloc(static_cast<size_t>(new_size)));
if (new_ptr) {
own_data_ = true;
}
}
if (new_ptr) {
data_ = new_ptr;
size_ = new_size;
} else {
FURY_CHECK(false) << "Out of memory when grow buffer, needed_size "
<< size_;
}
}
}
/// Copy a section of the buffer into a new Buffer.
void Copy(uint32_t start, uint32_t nbytes,
std::shared_ptr<Buffer> &out) const;
/// Copy a section of the buffer into a new Buffer.
void Copy(uint32_t start, uint32_t nbytes, Buffer &out) const;
/// Copy a section of the buffer.
void Copy(uint32_t start, uint32_t nbytes, uint8_t *out) const;
/// Copy a section of the buffer.
void Copy(uint32_t start, uint32_t nbytes, uint8_t *out,
uint32_t offset) const;
/// Copy data from `src` yo buffer
void CopyFrom(uint32_t offset, const uint8_t *src, uint32_t src_offset,
uint32_t nbytes);
/// Zero all bytes in padding
void ZeroPadding() {
// A zero-size buffer_ can have a null data pointer
if (size_ != 0) {
memset(data_, 0, static_cast<size_t>(size_));
}
}
/// \brief Copy buffer contents into a new std::string
/// \return std::string
/// \note Can throw std::bad_alloc if buffer is large
std::string ToString() const;
std::string Hex() const;
private:
uint8_t *data_;
uint32_t size_;
bool own_data_;
uint32_t writer_index_;
uint32_t reader_index_;
};
/// \brief Allocate a fixed-size mutable buffer from the default memory pool
///
/// \param[in] size size of buffer to allocate
/// \param[out] out the allocated buffer (contains padding)
///
/// \return success or not
bool AllocateBuffer(uint32_t size, std::shared_ptr<Buffer> *out);
bool AllocateBuffer(uint32_t size, Buffer **out);
Buffer *AllocateBuffer(uint32_t size);
} // namespace fury