trisquel-icecat/icecat/third_party/jpeg-xl/lib/jxl/padded_bytes.h

231 lines
7 KiB
C++

// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_JXL_BASE_PADDED_BYTES_H_
#define LIB_JXL_BASE_PADDED_BYTES_H_
// std::vector replacement with padding to reduce bounds checks in WriteBits
#include <jxl/memory_manager.h>
#include <algorithm> // max
#include <cstddef>
#include <cstdint>
#include <cstring> // memcpy
#include <initializer_list>
#include <utility> // swap
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/memory_manager_internal.h"
namespace jxl {
// Provides a subset of the std::vector interface with some differences:
// - allows BitWriter to write 64 bits at a time without bounds checking;
// - ONLY zero-initializes the first byte (required by BitWriter);
// - ensures cache-line alignment.
class PaddedBytes {
public:
// Required for output params.
explicit PaddedBytes(JxlMemoryManager* memory_manager)
: memory_manager_(memory_manager), size_(0), capacity_(0) {}
PaddedBytes(JxlMemoryManager* memory_manager, size_t size)
: memory_manager_(memory_manager), size_(size), capacity_(0) {
reserve(size);
}
PaddedBytes(JxlMemoryManager* memory_manager, size_t size, uint8_t value)
: memory_manager_(memory_manager), size_(size), capacity_(0) {
reserve(size);
if (size_ != 0) {
memset(data(), value, size);
}
}
PaddedBytes(const PaddedBytes& other)
: memory_manager_(other.memory_manager_),
size_(other.size_),
capacity_(0) {
reserve(size_);
if (data() != nullptr) memcpy(data(), other.data(), size_);
}
PaddedBytes& operator=(const PaddedBytes& other) {
if (this == &other) return *this;
resize(other.size());
if (data() != nullptr) memmove(data(), other.data(), size_);
return *this;
}
// default is not OK - need to set other.size_ to 0!
PaddedBytes(PaddedBytes&& other) noexcept
: memory_manager_(other.memory_manager_),
size_(other.size_),
capacity_(other.capacity_),
data_(std::move(other.data_)) {
other.size_ = other.capacity_ = 0;
}
PaddedBytes& operator=(PaddedBytes&& other) noexcept {
memory_manager_ = other.memory_manager_;
size_ = other.size_;
capacity_ = other.capacity_;
data_ = std::move(other.data_);
if (&other != this) {
other.size_ = other.capacity_ = 0;
}
return *this;
}
JxlMemoryManager* memory_manager() const { return memory_manager_; }
void swap(PaddedBytes& other) noexcept {
std::swap(memory_manager_, other.memory_manager_);
std::swap(size_, other.size_);
std::swap(capacity_, other.capacity_);
std::swap(data_, other.data_);
}
// If current capacity is greater than requested, then no-op. Otherwise
// copies existing data to newly allocated "data_". If allocation fails,
// data() == nullptr and size_ = capacity_ = 0.
// The new capacity will be at least 1.5 times the old capacity. This ensures
// that we avoid quadratic behaviour.
void reserve(size_t capacity) {
if (capacity <= capacity_) return;
size_t new_capacity = std::max(capacity, 3 * capacity_ / 2);
new_capacity = std::max<size_t>(64, new_capacity);
// BitWriter writes up to 7 bytes past the end.
StatusOr<AlignedMemory> new_data_or =
AlignedMemory::Create(memory_manager_, new_capacity + 8);
if (!new_data_or.ok()) {
// Allocation failed, discard all data to ensure this is noticed.
size_ = capacity_ = 0;
return;
}
AlignedMemory new_data = std::move(new_data_or).value();
if (data_.address<void>() == nullptr) {
// First allocation: ensure first byte is initialized (won't be copied).
new_data.address<uint8_t>()[0] = 0;
} else {
// Subsequent resize: copy existing data to new location.
memmove(new_data.address<void>(), data_.address<void>(), size_);
// Ensure that the first new byte is initialized, to allow write_bits to
// safely append to the newly-resized PaddedBytes.
new_data.address<uint8_t>()[size_] = 0;
}
capacity_ = new_capacity;
data_ = std::move(new_data);
}
// NOTE: unlike vector, this does not initialize the new data!
// However, we guarantee that write_bits can safely append after
// the resize, as we zero-initialize the first new byte of data.
// If size < capacity(), does not invalidate the memory.
void resize(size_t size) {
reserve(size);
size_ = (data() == nullptr) ? 0 : size;
}
// resize(size) plus explicit initialization of the new data with `value`.
void resize(size_t size, uint8_t value) {
size_t old_size = size_;
resize(size);
if (size_ > old_size) {
memset(data() + old_size, value, size_ - old_size);
}
}
// Amortized constant complexity due to exponential growth.
void push_back(uint8_t x) {
if (size_ == capacity_) {
reserve(capacity_ + 1);
if (data() == nullptr) return;
}
data_.address<uint8_t>()[size_++] = x;
}
size_t size() const { return size_; }
size_t capacity() const { return capacity_; }
uint8_t* data() { return data_.address<uint8_t>(); }
const uint8_t* data() const { return data_.address<uint8_t>(); }
// std::vector operations implemented in terms of the public interface above.
void clear() { resize(0); }
bool empty() const { return size() == 0; }
void assign(std::initializer_list<uint8_t> il) {
resize(il.size());
memcpy(data(), il.begin(), il.size());
}
uint8_t* begin() { return data(); }
const uint8_t* begin() const { return data(); }
uint8_t* end() { return begin() + size(); }
const uint8_t* end() const { return begin() + size(); }
uint8_t& operator[](const size_t i) {
BoundsCheck(i);
return data()[i];
}
const uint8_t& operator[](const size_t i) const {
BoundsCheck(i);
return data()[i];
}
uint8_t& back() {
JXL_ASSERT(size() != 0);
return data()[size() - 1];
}
const uint8_t& back() const {
JXL_ASSERT(size() != 0);
return data()[size() - 1];
}
template <typename T>
void append(const T& other) {
append(reinterpret_cast<const uint8_t*>(other.data()),
reinterpret_cast<const uint8_t*>(other.data()) + other.size());
}
void append(const uint8_t* begin, const uint8_t* end) {
if (end - begin > 0) {
size_t old_size = size();
resize(size() + (end - begin));
memcpy(data() + old_size, begin, end - begin);
}
}
private:
void BoundsCheck(size_t i) const {
// <= is safe due to padding and required by BitWriter.
JXL_ASSERT(i <= size());
}
JxlMemoryManager* memory_manager_;
size_t size_;
size_t capacity_;
AlignedMemory data_;
};
template <typename T>
static inline void Append(const T& s, PaddedBytes* out,
size_t* JXL_RESTRICT byte_pos) {
memcpy(out->data() + *byte_pos, s.data(), s.size());
*byte_pos += s.size();
JXL_CHECK(*byte_pos <= out->size());
}
} // namespace jxl
#endif // LIB_JXL_BASE_PADDED_BYTES_H_