mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 21:35:45 +02:00
lib: introduce ByteVector as a replacement for vector<uint8_t> and Spanner
After some benchmarking, it turns out that std::vector<uint8_t> is about 160 times slower than malloc(). malloc() is faster than "new uint8_t[]" too. Get rid of std:;vector<uint8_t> and replace it with a lightweight wrapper around malloc(), free(), and memcpy(). ByteVector has helpful methods for the common case of moving data to and from ioctl calls that use a fixed-length header placed contiguously with a variable-length input/output buffer. Data bytes are shared between copied ByteVector objects, allowing a large single buffer to be cheaply chopped up into smaller objects without memory copies. ByteVector implements the more useful parts of the std::vector API, so it can replace std::vector objects without needing an awkward adaptor class like Spanner. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
parent
2e36dd2d58
commit
f0eb9b202f
71
include/crucible/bytevector.h
Normal file
71
include/crucible/bytevector.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#ifndef _CRUCIBLE_BYTEVECTOR_H_
|
||||||
|
#define _CRUCIBLE_BYTEVECTOR_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace crucible {
|
||||||
|
using namespace std;
|
||||||
|
// new[] is a little slower than malloc
|
||||||
|
// shared_ptr is about 2x slower than unique_ptr
|
||||||
|
// vector<uint8_t> is ~160x slower
|
||||||
|
// so we won't bother with unique_ptr because we can't do shared copies with it
|
||||||
|
|
||||||
|
class ByteVector {
|
||||||
|
public:
|
||||||
|
using Pointer = shared_ptr<uint8_t>;
|
||||||
|
using value_type = Pointer::element_type;
|
||||||
|
using iterator = value_type*;
|
||||||
|
|
||||||
|
ByteVector() = default;
|
||||||
|
ByteVector(size_t size);
|
||||||
|
ByteVector(const ByteVector &that, size_t start, size_t length);
|
||||||
|
ByteVector(iterator begin, iterator end, size_t min_size = 0);
|
||||||
|
|
||||||
|
ByteVector at(size_t start, size_t length) const;
|
||||||
|
|
||||||
|
value_type& at(size_t) const;
|
||||||
|
iterator begin() const;
|
||||||
|
void clear();
|
||||||
|
value_type* data() const;
|
||||||
|
bool empty() const;
|
||||||
|
iterator end() const;
|
||||||
|
value_type& operator[](size_t) const;
|
||||||
|
size_t size() const;
|
||||||
|
bool operator==(const ByteVector &that) const;
|
||||||
|
|
||||||
|
// this version of erase only works at the beginning or end of the buffer, else throws exception
|
||||||
|
void erase(iterator first);
|
||||||
|
void erase(iterator first, iterator last);
|
||||||
|
|
||||||
|
// An important use case is ioctls that have a fixed-size header struct
|
||||||
|
// followed by a buffer for further arguments. These templates avoid
|
||||||
|
// doing reinterpret_casts every time.
|
||||||
|
template <class T> ByteVector(const T& object, size_t min_size);
|
||||||
|
template <class T> T* get() const;
|
||||||
|
private:
|
||||||
|
Pointer m_ptr;
|
||||||
|
size_t m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
ByteVector::ByteVector(const T& object, size_t min_size)
|
||||||
|
{
|
||||||
|
const auto size = max(min_size, sizeof(T));
|
||||||
|
m_ptr = Pointer(static_cast<value_type*>(malloc(size)), free);
|
||||||
|
memcpy(m_ptr.get(), &object, sizeof(T));
|
||||||
|
m_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T*
|
||||||
|
ByteVector::get() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<T*>(data());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _CRUCIBLE_BYTEVECTOR_H_
|
@ -4,6 +4,7 @@ default: libcrucible.a
|
|||||||
%.a: Makefile
|
%.a: Makefile
|
||||||
|
|
||||||
CRUCIBLE_OBJS = \
|
CRUCIBLE_OBJS = \
|
||||||
|
bytevector.o \
|
||||||
chatter.o \
|
chatter.o \
|
||||||
city.o \
|
city.o \
|
||||||
cleanup.o \
|
cleanup.o \
|
||||||
|
135
lib/bytevector.cc
Normal file
135
lib/bytevector.cc
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#include "crucible/bytevector.h"
|
||||||
|
|
||||||
|
#include "crucible/error.h"
|
||||||
|
|
||||||
|
namespace crucible {
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ByteVector::iterator
|
||||||
|
ByteVector::begin() const
|
||||||
|
{
|
||||||
|
return m_ptr.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector::iterator
|
||||||
|
ByteVector::end() const
|
||||||
|
{
|
||||||
|
return m_ptr.get() + m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
ByteVector::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ByteVector::empty() const
|
||||||
|
{
|
||||||
|
return !m_ptr || !m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ByteVector::clear()
|
||||||
|
{
|
||||||
|
m_ptr.reset();
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector::value_type&
|
||||||
|
ByteVector::operator[](size_t size) const
|
||||||
|
{
|
||||||
|
return m_ptr.get()[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector::ByteVector(const ByteVector &that, size_t start, size_t length)
|
||||||
|
{
|
||||||
|
THROW_CHECK0(out_of_range, that.m_ptr);
|
||||||
|
THROW_CHECK2(out_of_range, start, that.m_size, start < that.m_size);
|
||||||
|
THROW_CHECK2(out_of_range, start + length, that.m_size + length, start + length < that.m_size + length);
|
||||||
|
m_ptr = Pointer(that.m_ptr, that.m_ptr.get() + start);
|
||||||
|
m_size = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector
|
||||||
|
ByteVector::at(size_t start, size_t length) const
|
||||||
|
{
|
||||||
|
return ByteVector(*this, start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector::value_type&
|
||||||
|
ByteVector::at(size_t size) const
|
||||||
|
{
|
||||||
|
THROW_CHECK0(out_of_range, m_ptr);
|
||||||
|
THROW_CHECK2(out_of_range, size, m_size, size < m_size);
|
||||||
|
return m_ptr.get()[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector::ByteVector(size_t size)
|
||||||
|
{
|
||||||
|
m_ptr = Pointer(static_cast<value_type*>(malloc(size)), free);
|
||||||
|
// bad_alloc doesn't fit THROW_CHECK's template
|
||||||
|
THROW_CHECK0(runtime_error, m_ptr);
|
||||||
|
m_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector::ByteVector(iterator begin, iterator end, size_t min_size)
|
||||||
|
{
|
||||||
|
const size_t size = end - begin;
|
||||||
|
const size_t alloc_size = max(size, min_size);
|
||||||
|
m_ptr = Pointer(static_cast<value_type*>(malloc(alloc_size)), free);
|
||||||
|
THROW_CHECK0(runtime_error, m_ptr);
|
||||||
|
m_size = alloc_size;
|
||||||
|
memcpy(m_ptr.get(), begin, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ByteVector::operator==(const ByteVector &that) const
|
||||||
|
{
|
||||||
|
if (!m_ptr) {
|
||||||
|
return !that.m_ptr;
|
||||||
|
}
|
||||||
|
if (!that.m_ptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_size != that.m_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_ptr.get() == that.m_ptr.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !memcmp(m_ptr.get(), that.m_ptr.get(), m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ByteVector::erase(iterator begin, iterator end)
|
||||||
|
{
|
||||||
|
const size_t size = end - begin;
|
||||||
|
if (!size) return;
|
||||||
|
THROW_CHECK0(out_of_range, m_ptr);
|
||||||
|
const iterator my_begin = m_ptr.get();
|
||||||
|
const iterator my_end = my_begin + m_size;
|
||||||
|
THROW_CHECK4(out_of_range, my_begin, begin, my_end, end, my_begin == begin || my_end == end);
|
||||||
|
if (begin == my_begin) {
|
||||||
|
if (end == my_end) {
|
||||||
|
m_size = 0;
|
||||||
|
m_ptr.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_ptr = Pointer(m_ptr, end);
|
||||||
|
}
|
||||||
|
m_size -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ByteVector::erase(iterator begin)
|
||||||
|
{
|
||||||
|
erase(begin, begin + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector::value_type*
|
||||||
|
ByteVector::data() const
|
||||||
|
{
|
||||||
|
return m_ptr.get();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user