#include "crucible/bytevector.h" #include "crucible/error.h" #include "crucible/hexdump.h" #include "crucible/string.h" #include namespace crucible { using namespace std; ByteVector::iterator ByteVector::begin() const { unique_lock lock(m_mutex); return m_ptr.get(); } ByteVector::iterator ByteVector::end() const { unique_lock lock(m_mutex); 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() { unique_lock lock(m_mutex); m_ptr.reset(); m_size = 0; } ByteVector::value_type& ByteVector::operator[](size_t index) const { unique_lock lock(m_mutex); return m_ptr.get()[index]; } ByteVector::ByteVector(const ByteVector &that) { unique_lock lock(that.m_mutex); m_ptr = that.m_ptr; m_size = that.m_size; } ByteVector& ByteVector::operator=(const ByteVector &that) { // If &that == this, there's no need to do anything, but // especially don't try to lock the same mutex twice. if (&m_mutex != &that.m_mutex) { unique_lock lock_this(m_mutex, defer_lock); unique_lock lock_that(that.m_mutex, defer_lock); lock(lock_this, lock_that); m_ptr = that.m_ptr; m_size = that.m_size; } return *this; } 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 { unique_lock lock(m_mutex); THROW_CHECK0(out_of_range, m_ptr); THROW_CHECK2(out_of_range, size, m_size, size < m_size); return m_ptr.get()[size]; } static void * bv_allocate(size_t size) { #ifdef BEES_VALGRIND // XXX: only do this to shut up valgrind return calloc(1, size); #else return malloc(size); #endif } ByteVector::ByteVector(size_t size) { m_ptr = Pointer(static_cast(bv_allocate(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(bv_allocate(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 { unique_lock lock_this(m_mutex, defer_lock); unique_lock lock_that(that.m_mutex, defer_lock); lock(lock_this, lock_that); 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) { unique_lock lock(m_mutex); 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 { unique_lock lock(m_mutex); return m_ptr.get(); } ostream& operator<<(ostream &os, const ByteVector &bv) { hexdump(os, bv); return os; } }