blob: 2fb274c6118ed563d7ea40e13ebd876648764206 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
class ByteOffset {
public:
ByteOffset(const ByteOffset& other) : value_(other.value_), capacity_(other.capacity_) {}
static ByteOffset FromBuffer(size_t offset, size_t capacity) {
return ByteOffset(offset, capacity).AssertValid();
}
static ByteOffset Unbounded(size_t offset) { return ByteOffset(offset, -1); }
size_t unsafe_get() const { return value_; }
size_t capacity() const { return capacity_; }
ByteOffset AssertAlignedTo(size_t size) const {
assert((value_ % size) == 0);
if (!((value_ % size) == 0)) {
abort();
}
return *this;
}
ByteOffset operator+(size_t offset) const {
return ByteOffset(value_ + offset, capacity_).AssertValid();
}
ByteOffset() = delete;
private:
ByteOffset(size_t value, size_t capacity) : value_(value), capacity_(capacity) {}
ByteOffset& AssertValid() {
if (!((capacity_ == 0) && (value_ == 0))) {
assert(value_ < capacity_);
if (!(value_ < capacity_)) {
abort();
}
}
return *this;
}
size_t value_;
size_t capacity_;
};
template <typename T>
class WordOffset {
public:
WordOffset() = delete;
WordOffset(WordOffset& other) {
capacity_ = other.capacity_;
value_ = other.value_;
}
WordOffset operator+(size_t offset) const {
return WordOffset(value_ + offset, capacity_).AssertValid();
}
WordOffset operator+(WordOffset offset) const {
return WordOffset(value_ + offset.value_, capacity_).AssertValid();
}
WordOffset AddPadded(const ByteOffset& byte_offset) {
size_t needs_padding = (byte_offset.unsafe_get() % sizeof(T)) > 0;
// Multiply by needs_padding to set padding to 0 if no padding
// is necessary. This avoids unnecessary branching.
return WordOffset(value_ + (byte_offset.unsafe_get() / sizeof(T)) + needs_padding, capacity_);
}
WordOffset begin() { return WordOffset(0, capacity_); }
size_t capacity() { return capacity_; }
static WordOffset FromByteOffset(const ByteOffset& value) {
return WordOffset(value.AssertAlignedTo(sizeof(T)).unsafe_get() / sizeof(T),
value.capacity() / sizeof(T));
}
size_t unsafe_get() const { return value_; }
bool in_bounds(WordOffset<T> offset) { return !((offset.value_ + value_) >= capacity_); }
ByteOffset ToByteOffset() const {
return ByteOffset::FromBuffer(value_ * sizeof(T), capacity_ * sizeof(T));
}
void reset() { value_ = 0; }
static WordOffset invalid() { return WordOffset(0, 0); }
private:
WordOffset(size_t value, size_t capacity) : capacity_(capacity), value_(value) {}
WordOffset& AssertValid() {
if (!((capacity_ == 0) && (value_ == 0))) {
if (!(value_ < capacity_)) {
abort();
}
assert(value_ < capacity_);
}
return *this;
}
size_t capacity_;
size_t value_;
};
template <typename T>
WordOffset<T> WritePaddedInternal(T* buffer, const void* msg, const ByteOffset& length) {
size_t needs_padding = (length.unsafe_get() % sizeof(T)) > 0;
size_t padding = sizeof(T) - (length.unsafe_get() % sizeof(T));
// Multiply by needs_padding to set padding to 0 if no padding
// is necessary. This avoids unnecessary branching.
padding *= needs_padding;
// If we added padding -- zero the padding bytes in a single write operation
size_t is_nonzero_length = length.unsafe_get() != 0;
size_t eof_in_bytes = length.unsafe_get() + padding;
size_t eof_in_words = eof_in_bytes / sizeof(T);
size_t last_word = eof_in_words - is_nonzero_length;
// Set the last word in the buffer to zero before writing
// the data to it if we added padding. If we didn't add padding,
// multiply by 1 which ends up writing back the current contents of that word
// resulting in a NOP.
buffer[last_word] *= !needs_padding;
memcpy(buffer, msg, length.unsafe_get());
return WordOffset<T>::FromByteOffset(ByteOffset::Unbounded(length.unsafe_get() + padding));
}