From e76047365e64ceb45c99d22b95e2b220d7f411bb Mon Sep 17 00:00:00 2001 From: Lexi Mayfield Date: Sun, 3 Mar 2024 15:54:52 -0500 Subject: [PATCH] Add ReadExact/TryReadExact --- include/lexio/bufreader.hpp | 1 - include/lexio/core.hpp | 52 ++++++++++++++++++++++- include/lexio/serialize/float.hpp | 1 - include/lexio/serialize/int.hpp | 7 +--- include/lexio/serialize/tryfloat.hpp | 2 - include/lexio/serialize/tryint.hpp | 39 +++++------------ include/lexio/serialize/varint.hpp | 1 - include/lexio/stream/file.hpp | 2 - include/lexio/stream/vector.hpp | 1 - include/lexio/stream/view.hpp | 2 - include/lexio/try.hpp | 62 +++++++++++++++++++++++++++- tests/test.h | 1 - 12 files changed, 124 insertions(+), 47 deletions(-) diff --git a/include/lexio/bufreader.hpp b/include/lexio/bufreader.hpp index d7756d3..fe3da0d 100644 --- a/include/lexio/bufreader.hpp +++ b/include/lexio/bufreader.hpp @@ -24,7 +24,6 @@ #include "./core.hpp" #include -#include namespace LexIO { diff --git a/include/lexio/core.hpp b/include/lexio/core.hpp index 4ed9969..17334d0 100644 --- a/include/lexio/core.hpp +++ b/include/lexio/core.hpp @@ -133,6 +133,7 @@ #include #include #include +#include #include #include @@ -1038,9 +1039,58 @@ LEXIO_FORCEINLINE size_t Read(BYTE (&outArray)[N], const ReaderRef &reader) return Read(outArray, reader, N); } +/** + * @brief Read data from the current offset, inserting it into the passed + * buffer. Calls LexIO::RawRead as many times as necessary to fill + * the output buffer, throwing an exception if not enough bytes could + * be read. + * + * @param outDest Pointer to starting byte of output buffer. + * @param reader Reader to operate on. + * @param count Number of bytes to read. + * @throws std::runtime_error if stream encountered an EOF-like condition before + * enough bytes could be read, or if an error with the read operation + * was encountered. + */ +template && sizeof(BYTE) == 1>> +inline void ReadExact(BYTE *outDest, const ReaderRef &reader, size_t count) +{ + uint8_t *dest = reinterpret_cast(outDest); + size_t offset = 0, remain = count; + while (offset != count) + { + const size_t read = reader.LexRead(dest + offset, remain); + if (read == 0) + { + throw std::runtime_error("could not read exact number of bytes"); + } + + offset += read; + remain -= read; + } +} + +/** + * @brief Read data from the current offset, inserting it into the passed + * buffer. Calls LexIO::RawRead as many times as necessary to fill + * the output buffer, throwing an exception if not enough bytes could + * be read. + * + * @param outArray Output buffer array. + * @param reader Reader to operate on. + * @throws std::runtime_error if stream encountered an EOF-like condition before + * enough bytes could be read, or if an error with the read operation + * was encountered. + */ +template && sizeof(BYTE) == 1>> +LEXIO_FORCEINLINE void ReadExact(BYTE (&outArray)[N], const ReaderRef &reader) +{ + ReadExact(outArray, reader, N); +} + /** * @brief Get the current contents of the buffer. - * + * * @details LexIO::FillBuffer(0) should NEVER throw an exception. * * @param bufReader BufferedReader to operate on. diff --git a/include/lexio/serialize/float.hpp b/include/lexio/serialize/float.hpp index 643fc6c..3c157d2 100644 --- a/include/lexio/serialize/float.hpp +++ b/include/lexio/serialize/float.hpp @@ -23,7 +23,6 @@ #pragma once #include "./tryfloat.hpp" -#include namespace LexIO { diff --git a/include/lexio/serialize/int.hpp b/include/lexio/serialize/int.hpp index f720606..43ebea2 100644 --- a/include/lexio/serialize/int.hpp +++ b/include/lexio/serialize/int.hpp @@ -24,7 +24,6 @@ #include "../core.hpp" #include "./tryint.hpp" -#include namespace LexIO { @@ -41,11 +40,7 @@ namespace LexIO inline uint8_t ReadU8(const ReaderRef &reader) { uint8_t buf[sizeof(uint8_t)] = {0}; - const size_t count = Read(buf, reader); - if (count != sizeof(buf)) - { - throw std::runtime_error("could not read"); - } + ReadExact(buf, reader); return buf[0]; } diff --git a/include/lexio/serialize/tryfloat.hpp b/include/lexio/serialize/tryfloat.hpp index 35e22fd..873760e 100644 --- a/include/lexio/serialize/tryfloat.hpp +++ b/include/lexio/serialize/tryfloat.hpp @@ -23,8 +23,6 @@ #include "./tryint.hpp" -#include - namespace LexIO { diff --git a/include/lexio/serialize/tryint.hpp b/include/lexio/serialize/tryint.hpp index fb9559a..d875a7b 100644 --- a/include/lexio/serialize/tryint.hpp +++ b/include/lexio/serialize/tryint.hpp @@ -21,10 +21,9 @@ #pragma once -#include "../core.hpp" +#include "../try.hpp" #include -#include namespace LexIO { @@ -38,23 +37,15 @@ namespace LexIO * @param reader Reader to read from. * @return True if the read was successful. */ -inline bool TryReadU8(uint8_t &out, const ReaderRef &reader) +inline bool TryReadU8(uint8_t &out, const ReaderRef &reader) noexcept { - try - { - uint8_t buf[sizeof(uint8_t)] = {0}; - const size_t count = Read(buf, reader); - if (count != sizeof(buf)) - { - return false; - } - out = buf[0]; - return true; - } - catch (std::runtime_error &) + uint8_t buf[sizeof(uint8_t)] = {0}; + if (!TryReadExact(buf, reader)) { return false; } + out = buf[0]; + return true; } /** @@ -87,23 +78,15 @@ inline bool TryWriteU8(const WriterRef &writer, uint8_t value) * @param reader Reader to read from. * @return True if the read was successful. */ -inline bool TryRead8(int8_t &out, const ReaderRef &reader) +inline bool TryRead8(int8_t &out, const ReaderRef &reader) noexcept { - try - { - uint8_t buf[sizeof(uint8_t)] = {0}; - const size_t count = Read(buf, reader); - if (count != sizeof(buf)) - { - return false; - } - out = int8_t(buf[0]); - return true; - } - catch (std::runtime_error &) + uint8_t buf[sizeof(uint8_t)] = {0}; + if (!TryReadExact(buf, reader)) { return false; } + out = int8_t(buf[0]); + return true; } /** diff --git a/include/lexio/serialize/varint.hpp b/include/lexio/serialize/varint.hpp index 3dab07d..7935f02 100644 --- a/include/lexio/serialize/varint.hpp +++ b/include/lexio/serialize/varint.hpp @@ -23,7 +23,6 @@ #pragma once #include "./tryvarint.hpp" -#include namespace LexIO { diff --git a/include/lexio/stream/file.hpp b/include/lexio/stream/file.hpp index 580b1c9..7ec4a52 100644 --- a/include/lexio/stream/file.hpp +++ b/include/lexio/stream/file.hpp @@ -23,8 +23,6 @@ #include "../core.hpp" -#include - namespace LexIO { diff --git a/include/lexio/stream/vector.hpp b/include/lexio/stream/vector.hpp index 73f7bfe..0ef4cc1 100644 --- a/include/lexio/stream/vector.hpp +++ b/include/lexio/stream/vector.hpp @@ -23,7 +23,6 @@ #include "../core.hpp" -#include #include namespace LexIO diff --git a/include/lexio/stream/view.hpp b/include/lexio/stream/view.hpp index 809afae..9021c21 100644 --- a/include/lexio/stream/view.hpp +++ b/include/lexio/stream/view.hpp @@ -23,8 +23,6 @@ #include "../core.hpp" -#include - namespace LexIO { diff --git a/include/lexio/try.hpp b/include/lexio/try.hpp index b86ce87..adba3e3 100644 --- a/include/lexio/try.hpp +++ b/include/lexio/try.hpp @@ -26,7 +26,7 @@ namespace LexIO namespace Detail { -std::exception_ptr &LastError() noexcept +inline std::exception_ptr &LastError() noexcept { static thread_local std::exception_ptr ex; return ex; @@ -301,6 +301,66 @@ LEXIO_FORCEINLINE bool TryRead(size_t &outActual, BYTE (&outArray)[N], const Rea return TryRead(outActual, outArray, reader, N); } +/** + * @brief Read data from the current offset, inserting it into the passed + * buffer. Calls LexIO::RawRead as many times as necessary to fill + * the output buffer, returning failure if not enough bytes could + * be read. + * + * @param outDest Pointer to starting byte of output buffer. + * @param reader Reader to operate on. + * @param count Number of bytes to read. + * @return True if successful, false if stream encountered an EOF-like condition + * before enough bytes could be read, or if an error with the read + * operation was encountered. To get specific error, call + * LexIO::ThrowLastError. + */ +template && sizeof(BYTE) == 1>> +inline bool TryReadExact(BYTE *outDest, const ReaderRef &reader, size_t count) noexcept +{ + try + { + uint8_t *dest = reinterpret_cast(outDest); + size_t offset = 0, remain = count; + while (offset != count) + { + const size_t read = reader.LexRead(dest + offset, remain); + if (read == 0) + { + throw std::runtime_error("could not read exact number of bytes"); + } + + offset += read; + remain -= read; + } + return true; + } + catch (...) + { + SetLastError(std::current_exception()); + return false; + } +} + +/** + * @brief Read data from the current offset, inserting it into the passed + * buffer. Calls LexIO::RawRead as many times as necessary to fill + * the output buffer, returning failure if not enough bytes could + * be read. + * + * @param outArray Output buffer array. + * @param reader Reader to operate on. + * @return True if successful, false if stream encountered an EOF-like condition + * before enough bytes could be read, or if an error with the read + * operation was encountered. To get specific error, call + * LexIO::ThrowLastError. + */ +template && sizeof(BYTE) == 1>> +LEXIO_FORCEINLINE bool TryReadExact(BYTE (&outArray)[N], const ReaderRef &reader) noexcept +{ + return TryReadExact(outArray, reader, N); +} + /** * @brief Write a buffer of data at the current offset. Calls LexIO::RawWrite * as many times as necessary to write the entire buffer unless EOF diff --git a/tests/test.h b/tests/test.h index 07b77c1..213a8f9 100644 --- a/tests/test.h +++ b/tests/test.h @@ -18,7 +18,6 @@ #include #include -#include #include "config.h"