Made TypelessBuffer allocator-aware and split some of its functionality into MemoryView type.
This commit is contained in:
parent
59780ed6de
commit
8ad34427f3
@ -5,10 +5,10 @@
|
|||||||
#define MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED 1
|
#define MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED 1
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <cstddef>
|
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
#include "../debug/assert.hpp"
|
#include "../debug/assert.hpp"
|
||||||
#include "../util/traits.hpp"
|
#include "../internal/common.hpp"
|
||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
@ -25,60 +25,170 @@ namespace mijin
|
|||||||
// public types
|
// public types
|
||||||
//
|
//
|
||||||
|
|
||||||
template<typename TBytes>
|
template<typename T>
|
||||||
class MemoryViewBase
|
concept MemoryViewable = requires(const T& object)
|
||||||
|
{
|
||||||
|
{ object.data() } -> std::convertible_to<const void*>;
|
||||||
|
{ object.byteSize() } -> std::convertible_to<std::size_t>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept RWMemoryViewable = MemoryViewable<T> && requires(T& object)
|
||||||
|
{
|
||||||
|
{ object.data() } -> std::convertible_to<void*>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TConcrete>
|
||||||
|
class MixinMemoryView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr bool WRITABLE = requires(TConcrete& object) { { object.data() } -> std::convertible_to<void*>; };
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]]
|
||||||
|
auto makeSpan();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]]
|
||||||
|
auto makeSpan() const;
|
||||||
|
|
||||||
|
template<typename TChar = char, typename TTraits = std::char_traits<TChar>>
|
||||||
|
[[nodiscard]]
|
||||||
|
std::basic_string_view<TChar, TTraits> makeStringView() const ;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]]
|
||||||
|
auto& dataAt(std::size_t offset) MIJIN_NOEXCEPT;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]]
|
||||||
|
auto& dataAt(std::size_t offset) const MIJIN_NOEXCEPT;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto bytes() MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
using return_t = mijin::copy_const_t<std::remove_pointer_t<decltype(static_cast<TConcrete*>(this)->data())>, std::byte>;
|
||||||
|
return static_cast<return_t*>(static_cast<TConcrete*>(this)->data());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto bytes() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
using return_t = mijin::copy_const_t<std::remove_pointer_t<decltype(static_cast<const TConcrete*>(this)->data())>, std::byte>;
|
||||||
|
return static_cast<return_t*>(static_cast<const TConcrete*>(this)->data());
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::size_t byteSizeImpl() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return static_cast<const TConcrete*>(this)->byteSize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryView : public MixinMemoryView<MemoryView>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
private:
|
private:
|
||||||
std::span<TBytes> bytes_;
|
void* data_ = nullptr;
|
||||||
|
std::size_t byteSize_ = 0;
|
||||||
public:
|
public:
|
||||||
MemoryViewBase() noexcept = default;
|
MemoryView() noexcept = default;
|
||||||
MemoryViewBase(const MemoryViewBase&) noexcept = default;
|
MemoryView(const MemoryView&) = default;
|
||||||
MemoryViewBase(copy_const_t<TBytes, void>* data, std::size_t size) noexcept : bytes_(static_cast<TBytes*>(data), size) {}
|
MemoryView(void* data, std::size_t byteSize) MIJIN_NOEXCEPT : data_(data), byteSize_(byteSize) {}
|
||||||
|
template<typename T> requires(!std::is_const_v<T>)
|
||||||
|
MemoryView(std::span<T> span) MIJIN_NOEXCEPT : data_(span.data()), byteSize_(span.size_bytes()) {}
|
||||||
|
template<RWMemoryViewable T>
|
||||||
|
MemoryView(T& memoryViewable) MIJIN_NOEXCEPT : data_(memoryViewable.data()), byteSize_(memoryViewable.byteSize()) {}
|
||||||
|
|
||||||
MemoryViewBase& operator=(const MemoryViewBase&) noexcept = default;
|
MemoryView& operator=(const MemoryView&) = default;
|
||||||
|
|
||||||
[[nodiscard]] void* data() noexcept { return bytes_.data(); }
|
|
||||||
[[nodiscard]] const void* data() const noexcept { return bytes_.data(); }
|
|
||||||
[[nodiscard]] size_type byteSize() const noexcept { return bytes_.size(); }
|
|
||||||
[[nodiscard]] bool empty() const noexcept { return bytes_.empty(); }
|
|
||||||
|
|
||||||
template<typename T>
|
[[nodiscard]]
|
||||||
[[nodiscard]] std::span<T> makeSpan();
|
void* data() const MIJIN_NOEXCEPT { return data_; }
|
||||||
|
|
||||||
template<typename T>
|
[[nodiscard]]
|
||||||
[[nodiscard]] std::span<const T> makeSpan() const;
|
size_type byteSize() const MIJIN_NOEXCEPT { return byteSize_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConstMemoryView : public MixinMemoryView<ConstMemoryView>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = std::size_t;
|
||||||
|
private:
|
||||||
|
const void* data_;
|
||||||
|
std::size_t byteSize_;
|
||||||
|
public:
|
||||||
|
ConstMemoryView() noexcept = default;
|
||||||
|
ConstMemoryView(const ConstMemoryView&) = default;
|
||||||
|
ConstMemoryView(void* data, std::size_t byteSize) MIJIN_NOEXCEPT : data_(data), byteSize_(byteSize) {}
|
||||||
|
template<typename T>
|
||||||
|
ConstMemoryView(std::span<T> span) MIJIN_NOEXCEPT : data_(span.data()), byteSize_(span.size_bytes()) {}
|
||||||
|
template<MemoryViewable T>
|
||||||
|
ConstMemoryView(const T& memoryViewable) MIJIN_NOEXCEPT : data_(memoryViewable.data()), byteSize_(memoryViewable.byteSize()) {}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const void* data() const MIJIN_NOEXCEPT { return data_; }
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_type byteSize() const MIJIN_NOEXCEPT { return byteSize_; }
|
||||||
};
|
};
|
||||||
using MemoryView = MemoryViewBase<std::byte>;
|
|
||||||
using ConstMemoryView = MemoryViewBase<const std::byte>;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// public functions
|
// public functions
|
||||||
//
|
//
|
||||||
|
|
||||||
template<typename TBytes>
|
template<typename TConcrete>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::span<T> MemoryViewBase<TBytes>::makeSpan()
|
auto MixinMemoryView<TConcrete>::makeSpan()
|
||||||
{
|
{
|
||||||
static_assert(!std::is_const_v<TBytes>, "Cannot create writable spans from const memory views.");
|
MIJIN_ASSERT(byteSizeImpl() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type.");
|
||||||
MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "MemoryView cannot be divided into elements of this type.");
|
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||||
return {
|
return std::span<return_t>{
|
||||||
std::bit_cast<T*>(bytes_.data()),
|
std::bit_cast<return_t*>(bytes()),
|
||||||
std::bit_cast<T*>(bytes_.data() + bytes_.size())
|
std::bit_cast<return_t*>(bytes() + byteSizeImpl())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TBytes>
|
template<typename TConcrete>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::span<const T> MemoryViewBase<TBytes>::makeSpan() const
|
auto MixinMemoryView<TConcrete>::makeSpan() const
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "MemoryView cannot be divided into elements of this type.");
|
MIJIN_ASSERT(byteSizeImpl() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type.");
|
||||||
return {
|
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||||
std::bit_cast<const T*>(bytes_.data()),
|
return std::span<return_t>{
|
||||||
std::bit_cast<const T*>(bytes_.data() + bytes_.size())
|
std::bit_cast<return_t*>(bytes()),
|
||||||
|
std::bit_cast<return_t*>(bytes() + byteSizeImpl())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename TConcrete>
|
||||||
|
template<typename TChar, typename TTraits>
|
||||||
|
std::basic_string_view<TChar, TTraits> MixinMemoryView<TConcrete>::makeStringView() const
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(byteSizeImpl() % sizeof(TChar) == 0, "Buffer cannot be divided into elements of this char type.");
|
||||||
|
return {std::bit_cast<const TChar*>(bytes()), byteSizeImpl() / sizeof(TChar)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TConcrete>
|
||||||
|
template<typename T>
|
||||||
|
auto& MixinMemoryView<TConcrete>::dataAt(std::size_t offset) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(offset % alignof(T) == 0, "Offset must be correctly aligned.");
|
||||||
|
MIJIN_ASSERT(offset + sizeof(T) < byteSizeImpl(), "Buffer access out-of-range.");
|
||||||
|
|
||||||
|
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||||
|
return *std::bit_cast<return_t*>(bytes().data() + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TConcrete>
|
||||||
|
template<typename T>
|
||||||
|
auto& MixinMemoryView<TConcrete>::dataAt(std::size_t offset) const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(offset % alignof(T) == 0, "Offset must be correctly aligned.");
|
||||||
|
MIJIN_ASSERT(offset + sizeof(T) < byteSizeImpl(), "Buffer access out-of-range.");
|
||||||
|
|
||||||
|
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||||
|
return *std::bit_cast<return_t*>(bytes().data() + offset);
|
||||||
|
}
|
||||||
} // namespace mijin
|
} // namespace mijin
|
||||||
|
|
||||||
#endif // !defined(MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED)
|
#endif // !defined(MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED)
|
||||||
|
@ -10,7 +10,9 @@
|
|||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "./memory_view.hpp"
|
||||||
#include "../debug/assert.hpp"
|
#include "../debug/assert.hpp"
|
||||||
|
#include "../internal/common.hpp"
|
||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
@ -27,17 +29,26 @@ namespace mijin
|
|||||||
// public types
|
// public types
|
||||||
//
|
//
|
||||||
|
|
||||||
template<typename T>
|
template<typename T, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
class BufferView;
|
class BufferView;
|
||||||
|
|
||||||
class TypelessBuffer
|
template<template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
|
class BaseTypelessBuffer : public MixinMemoryView<BaseTypelessBuffer<TAllocator>>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
private:
|
private:
|
||||||
std::vector<std::byte> bytes_;
|
std::vector<std::byte, TAllocator<std::byte>> bytes_;
|
||||||
public:
|
public:
|
||||||
auto operator<=>(const TypelessBuffer&) const noexcept = default;
|
BaseTypelessBuffer() noexcept = default;
|
||||||
|
BaseTypelessBuffer(const BaseTypelessBuffer&) = default;
|
||||||
|
BaseTypelessBuffer(BaseTypelessBuffer&&) = default;
|
||||||
|
explicit BaseTypelessBuffer(TAllocator<std::byte> allocator) : bytes_(std::move(allocator)) {}
|
||||||
|
|
||||||
|
BaseTypelessBuffer& operator=(const BaseTypelessBuffer&) = default;
|
||||||
|
BaseTypelessBuffer& operator=(BaseTypelessBuffer&&) = default;
|
||||||
|
|
||||||
|
auto operator<=>(const BaseTypelessBuffer&) const noexcept = default;
|
||||||
|
|
||||||
[[nodiscard]] void* data() noexcept { return bytes_.data(); }
|
[[nodiscard]] void* data() noexcept { return bytes_.data(); }
|
||||||
[[nodiscard]] const void* data() const noexcept { return bytes_.data(); }
|
[[nodiscard]] const void* data() const noexcept { return bytes_.data(); }
|
||||||
@ -49,27 +60,14 @@ public:
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]] BufferView<T> makeBufferView() { return BufferView<T>(this); }
|
[[nodiscard]] BufferView<T> makeBufferView() { return BufferView<T>(this); }
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
[[nodiscard]] std::span<T> makeSpan();
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
[[nodiscard]] std::span<const T> makeSpan() const;
|
|
||||||
|
|
||||||
template<typename TChar = char, typename TTraits = std::char_traits<TChar>>
|
|
||||||
[[nodiscard]] std::basic_string_view<TChar, TTraits> makeStringView() const ;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void append(std::span<const T> data);
|
void append(std::span<const T> data);
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
[[nodiscard]] T& dataAt(size_type offset) MIJIN_NOEXCEPT;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
[[nodiscard]] const T& dataAt(size_type offset) const MIJIN_NOEXCEPT;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
using TypelessBuffer = BaseTypelessBuffer<>;
|
||||||
|
|
||||||
|
template<typename T, template<typename> typename TAllocator>
|
||||||
class BufferView
|
class BufferView
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -84,10 +82,10 @@ public:
|
|||||||
using iterator = T*;
|
using iterator = T*;
|
||||||
using const_iterator = const T*;
|
using const_iterator = const T*;
|
||||||
private:
|
private:
|
||||||
class TypelessBuffer* buffer_ = nullptr;
|
class BaseTypelessBuffer<TAllocator>* buffer_ = nullptr;
|
||||||
public:
|
public:
|
||||||
BufferView() = default;
|
BufferView() = default;
|
||||||
explicit BufferView(class TypelessBuffer* buffer) : buffer_(buffer) {}
|
explicit BufferView(class BaseTypelessBuffer<TAllocator>* buffer) : buffer_(buffer) {}
|
||||||
BufferView(const BufferView&) = default;
|
BufferView(const BufferView&) = default;
|
||||||
|
|
||||||
BufferView& operator=(const BufferView&) = default;
|
BufferView& operator=(const BufferView&) = default;
|
||||||
@ -131,57 +129,13 @@ public:
|
|||||||
// public functions
|
// public functions
|
||||||
//
|
//
|
||||||
|
|
||||||
|
template<template<typename> typename TAllocator>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::span<T> TypelessBuffer::makeSpan()
|
void BaseTypelessBuffer<TAllocator>::append(std::span<const T> data)
|
||||||
{
|
|
||||||
MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type.");
|
|
||||||
return {
|
|
||||||
std::bit_cast<T*>(bytes_.data()),
|
|
||||||
std::bit_cast<T*>(bytes_.data() + bytes_.size())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
std::span<const T> TypelessBuffer::makeSpan() const
|
|
||||||
{
|
|
||||||
MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type.");
|
|
||||||
return {
|
|
||||||
std::bit_cast<const T*>(bytes_.data()),
|
|
||||||
std::bit_cast<const T*>(bytes_.data() + bytes_.size())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename TChar, typename TTraits>
|
|
||||||
std::basic_string_view<TChar, TTraits> TypelessBuffer::makeStringView() const
|
|
||||||
{
|
|
||||||
MIJIN_ASSERT(bytes_.size() % sizeof(TChar) == 0, "Buffer cannot be divided into elements of this char type.");
|
|
||||||
return {std::bit_cast<const TChar*>(bytes_.data()), bytes_.size() / sizeof(TChar)};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void TypelessBuffer::append(std::span<const T> data)
|
|
||||||
{
|
{
|
||||||
bytes_.resize(bytes_.size() + data.size_bytes());
|
bytes_.resize(bytes_.size() + data.size_bytes());
|
||||||
std::memcpy(bytes_.data() + bytes_.size() - data.size_bytes(), data.data(), data.size_bytes());
|
std::memcpy(bytes_.data() + bytes_.size() - data.size_bytes(), data.data(), data.size_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T& TypelessBuffer::dataAt(size_type offset) MIJIN_NOEXCEPT
|
|
||||||
{
|
|
||||||
MIJIN_ASSERT(offset % alignof(T) == 0, "Offset must be correctly aligned.");
|
|
||||||
MIJIN_ASSERT(offset + sizeof(T) < bytes_.size(), "Buffer access out-of-range.");
|
|
||||||
|
|
||||||
return *std::bit_cast<T*>(bytes_.data() + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
const T& TypelessBuffer::dataAt(size_type offset) const MIJIN_NOEXCEPT
|
|
||||||
{
|
|
||||||
MIJIN_ASSERT(offset % alignof(T) == 0, "Offset must be correctly aligned.");
|
|
||||||
MIJIN_ASSERT(offset + sizeof(T) < bytes_.size(), "Buffer access out-of-range.");
|
|
||||||
|
|
||||||
return *std::bit_cast<const T*>(bytes_.data() + offset);
|
|
||||||
}
|
|
||||||
} // namespace mijin
|
} // namespace mijin
|
||||||
|
|
||||||
#endif // !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)
|
#endif // !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../detect.hpp"
|
#include "../detect.hpp"
|
||||||
|
#include "../util/string.hpp"
|
||||||
|
|
||||||
#if MIJIN_TARGET_OS != MIJIN_OS_WINDOWS && (MIJIN_COMPILER == MIJIN_COMPILER_CLANG || MIJIN_COMPILER == MIJIN_COMPILER_GCC)
|
#if MIJIN_TARGET_OS != MIJIN_OS_WINDOWS && (MIJIN_COMPILER == MIJIN_COMPILER_CLANG || MIJIN_COMPILER == MIJIN_COMPILER_GCC)
|
||||||
#define MIJIN_USE_LIBBACKTRACE 1
|
#define MIJIN_USE_LIBBACKTRACE 1
|
||||||
|
@ -161,76 +161,6 @@ StreamError Stream::getTotalLength(std::size_t& outLength)
|
|||||||
return StreamError::SUCCESS;
|
return StreamError::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamError Stream::readRest(TypelessBuffer& outBuffer)
|
|
||||||
{
|
|
||||||
// first try to allocate everything at once
|
|
||||||
std::size_t length = 0;
|
|
||||||
if (const StreamError lengthError = getTotalLength(length); lengthError == StreamError::SUCCESS)
|
|
||||||
{
|
|
||||||
MIJIN_ASSERT(getFeatures().tell, "How did you find the length if you cannot tell()?");
|
|
||||||
length -= tell();
|
|
||||||
outBuffer.resize(length);
|
|
||||||
if (const StreamError error = readRaw(outBuffer.data(), length); error != StreamError::SUCCESS)
|
|
||||||
{
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
return StreamError::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// could not determine the size, read chunk-wise
|
|
||||||
static constexpr std::size_t CHUNK_SIZE = 4096;
|
|
||||||
std::array<std::byte, CHUNK_SIZE> chunk = {};
|
|
||||||
|
|
||||||
while (!isAtEnd())
|
|
||||||
{
|
|
||||||
std::size_t bytesRead = 0;
|
|
||||||
if (const StreamError error = readRaw(chunk, {.partial = true}, &bytesRead); error != StreamError::SUCCESS)
|
|
||||||
{
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
outBuffer.resize(outBuffer.byteSize() + bytesRead);
|
|
||||||
const std::span<std::byte> bufferBytes = outBuffer.makeSpan<std::byte>();
|
|
||||||
std::copy_n(chunk.begin(), bytesRead, bufferBytes.end() - static_cast<long>(bytesRead));
|
|
||||||
}
|
|
||||||
return StreamError::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
mijin::Task<StreamError> Stream::c_readRest(TypelessBuffer& outBuffer)
|
|
||||||
{
|
|
||||||
// first try to allocate everything at once
|
|
||||||
std::size_t length = 0;
|
|
||||||
if (StreamError lengthError = getTotalLength(length); lengthError == StreamError::SUCCESS)
|
|
||||||
{
|
|
||||||
MIJIN_ASSERT(getFeatures().tell, "How did you find the length if you cannot tell()?");
|
|
||||||
length -= tell();
|
|
||||||
outBuffer.resize(length);
|
|
||||||
if (StreamError error = co_await c_readRaw(outBuffer.data(), length); error != StreamError::SUCCESS)
|
|
||||||
{
|
|
||||||
co_return error;
|
|
||||||
}
|
|
||||||
co_return StreamError::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// could not determine the size, read chunk-wise
|
|
||||||
static constexpr std::size_t CHUNK_SIZE = 4096;
|
|
||||||
std::array<std::byte, CHUNK_SIZE> chunk = {};
|
|
||||||
|
|
||||||
while (!isAtEnd())
|
|
||||||
{
|
|
||||||
std::size_t bytesRead = 0;
|
|
||||||
if (StreamError error = co_await c_readRaw(chunk, {.partial = true}, &bytesRead); error != StreamError::SUCCESS)
|
|
||||||
{
|
|
||||||
co_return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
outBuffer.resize(outBuffer.byteSize() + bytesRead);
|
|
||||||
std::span<std::byte> bufferBytes = outBuffer.makeSpan<std::byte>();
|
|
||||||
std::copy_n(chunk.begin(), bytesRead, bufferBytes.end() - static_cast<long>(bytesRead));
|
|
||||||
}
|
|
||||||
co_return StreamError::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamError Stream::readLine(std::string& outString)
|
StreamError Stream::readLine(std::string& outString)
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(getFeatures().readOptions.peek, "Stream needs to support peeking.");
|
MIJIN_ASSERT(getFeatures().readOptions.peek, "Stream needs to support peeking.");
|
||||||
|
@ -132,8 +132,9 @@ public:
|
|||||||
const std::size_t bytes = std::distance(range.begin(), range.end()) * sizeof(std::ranges::range_value_t<TRange>);
|
const std::size_t bytes = std::distance(range.begin(), range.end()) * sizeof(std::ranges::range_value_t<TRange>);
|
||||||
return readRaw(&*range.begin(), bytes, {.partial = partial}, outBytesRead);
|
return readRaw(&*range.begin(), bytes, {.partial = partial}, outBytesRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamError readRaw(TypelessBuffer& buffer, const ReadOptions& options = {})
|
template<template<typename> typename TAllocator>
|
||||||
|
StreamError readRaw(BaseTypelessBuffer<TAllocator>& buffer, const ReadOptions& options = {})
|
||||||
{
|
{
|
||||||
return readRaw(buffer.data(), buffer.byteSize(), options);
|
return readRaw(buffer.data(), buffer.byteSize(), options);
|
||||||
}
|
}
|
||||||
@ -144,8 +145,9 @@ public:
|
|||||||
const std::size_t bytes = std::distance(range.begin(), range.end()) * sizeof(std::ranges::range_value_t<TRange>);
|
const std::size_t bytes = std::distance(range.begin(), range.end()) * sizeof(std::ranges::range_value_t<TRange>);
|
||||||
return c_readRaw(&*range.begin(), bytes, options, outBytesRead);
|
return c_readRaw(&*range.begin(), bytes, options, outBytesRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
mijin::Task<StreamError> c_readRaw(TypelessBuffer& buffer, const ReadOptions& options = {})
|
template<template<typename> typename TAllocator>
|
||||||
|
mijin::Task<StreamError> c_readRaw(BaseTypelessBuffer<TAllocator>& buffer, const ReadOptions& options = {})
|
||||||
{
|
{
|
||||||
return c_readRaw(buffer.data(), buffer.byteSize(), options);
|
return c_readRaw(buffer.data(), buffer.byteSize(), options);
|
||||||
}
|
}
|
||||||
@ -269,8 +271,12 @@ public:
|
|||||||
inline StreamError writeString(std::string_view str) { return writeBinaryString(str); }
|
inline StreamError writeString(std::string_view str) { return writeBinaryString(str); }
|
||||||
|
|
||||||
StreamError getTotalLength(std::size_t& outLength);
|
StreamError getTotalLength(std::size_t& outLength);
|
||||||
StreamError readRest(TypelessBuffer& outBuffer);
|
|
||||||
mijin::Task<StreamError> c_readRest(TypelessBuffer& outBuffer);
|
template<template<typename> typename TAllocator>
|
||||||
|
StreamError readRest(BaseTypelessBuffer<TAllocator>& outBuffer);
|
||||||
|
|
||||||
|
template<template<typename> typename TAllocator>
|
||||||
|
mijin::Task<StreamError> c_readRest(BaseTypelessBuffer<TAllocator>& outBuffer);
|
||||||
|
|
||||||
StreamError readLine(std::string& outString);
|
StreamError readLine(std::string& outString);
|
||||||
mijin::Task<StreamError> c_readLine(std::string& outString);
|
mijin::Task<StreamError> c_readLine(std::string& outString);
|
||||||
@ -360,6 +366,78 @@ using StreamResult = ResultBase<TSuccess, StreamError>;
|
|||||||
// public functions
|
// public functions
|
||||||
//
|
//
|
||||||
|
|
||||||
|
template<template<typename> typename TAllocator>
|
||||||
|
StreamError Stream::readRest(BaseTypelessBuffer<TAllocator>& outBuffer)
|
||||||
|
{
|
||||||
|
// first try to allocate everything at once
|
||||||
|
std::size_t length = 0;
|
||||||
|
if (const StreamError lengthError = getTotalLength(length); lengthError == StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(getFeatures().tell, "How did you find the length if you cannot tell()?");
|
||||||
|
length -= tell();
|
||||||
|
outBuffer.resize(length);
|
||||||
|
if (const StreamError error = readRaw(outBuffer.data(), length); error != StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return StreamError::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// could not determine the size, read chunk-wise
|
||||||
|
static constexpr std::size_t CHUNK_SIZE = 4096;
|
||||||
|
std::array<std::byte, CHUNK_SIZE> chunk = {};
|
||||||
|
|
||||||
|
while (!isAtEnd())
|
||||||
|
{
|
||||||
|
std::size_t bytesRead = 0;
|
||||||
|
if (const StreamError error = readRaw(chunk, {.partial = true}, &bytesRead); error != StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
outBuffer.resize(outBuffer.byteSize() + bytesRead);
|
||||||
|
const std::span<std::byte> bufferBytes = outBuffer.template makeSpan<std::byte>();
|
||||||
|
std::copy_n(chunk.begin(), bytesRead, bufferBytes.end() - static_cast<long>(bytesRead));
|
||||||
|
}
|
||||||
|
return StreamError::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<template<typename> typename TAllocator>
|
||||||
|
mijin::Task<StreamError> Stream::c_readRest(BaseTypelessBuffer<TAllocator>& outBuffer)
|
||||||
|
{
|
||||||
|
// first try to allocate everything at once
|
||||||
|
std::size_t length = 0;
|
||||||
|
if (StreamError lengthError = getTotalLength(length); lengthError == StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(getFeatures().tell, "How did you find the length if you cannot tell()?");
|
||||||
|
length -= tell();
|
||||||
|
outBuffer.resize(length);
|
||||||
|
if (StreamError error = co_await c_readRaw(outBuffer.data(), length); error != StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
co_return error;
|
||||||
|
}
|
||||||
|
co_return StreamError::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// could not determine the size, read chunk-wise
|
||||||
|
static constexpr std::size_t CHUNK_SIZE = 4096;
|
||||||
|
std::array<std::byte, CHUNK_SIZE> chunk = {};
|
||||||
|
|
||||||
|
while (!isAtEnd())
|
||||||
|
{
|
||||||
|
std::size_t bytesRead = 0;
|
||||||
|
if (StreamError error = co_await c_readRaw(chunk, {.partial = true}, &bytesRead); error != StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
co_return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
outBuffer.resize(outBuffer.byteSize() + bytesRead);
|
||||||
|
std::span<std::byte> bufferBytes = outBuffer.template makeSpan<std::byte>();
|
||||||
|
std::copy_n(chunk.begin(), bytesRead, bufferBytes.end() - static_cast<long>(bytesRead));
|
||||||
|
}
|
||||||
|
co_return StreamError::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename TChar>
|
template<typename TChar>
|
||||||
StreamError Stream::readAsString(std::basic_string<TChar>& outString)
|
StreamError Stream::readAsString(std::basic_string<TChar>& outString)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,7 @@ struct HTTPRequestOptions
|
|||||||
{
|
{
|
||||||
std::string method = "GET";
|
std::string method = "GET";
|
||||||
std::multimap<std::string, std::string> headers;
|
std::multimap<std::string, std::string> headers;
|
||||||
TypelessBuffer body;
|
BaseTypelessBuffer<std::allocator> body;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HTTPResponse
|
struct HTTPResponse
|
||||||
@ -53,7 +53,7 @@ struct HTTPResponse
|
|||||||
unsigned status;
|
unsigned status;
|
||||||
std::string statusMessage;
|
std::string statusMessage;
|
||||||
std::multimap<std::string, std::string> headers;
|
std::multimap<std::string, std::string> headers;
|
||||||
TypelessBuffer body;
|
BaseTypelessBuffer<std::allocator> body;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HTTPStream
|
class HTTPStream
|
||||||
|
Loading…
x
Reference in New Issue
Block a user