summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt8
-rw-r--r--README.org1
-rw-r--r--cmake/warnings.cmake7
-rw-r--r--include/fud_algorithm.hpp14
-rw-r--r--include/fud_allocator.hpp46
-rw-r--r--include/fud_csv.hpp82
-rw-r--r--include/fud_file.hpp6
-rw-r--r--include/fud_print.hpp40
-rw-r--r--include/fud_text.hpp50
-rw-r--r--include/fud_type_traits.hpp (renamed from include/fud_fud_type_traits.hpp)0
-rw-r--r--source/fud_assert.cpp8
-rw-r--r--source/fud_csv.cpp92
-rw-r--r--source/fud_file.cpp76
-rw-r--r--source/fud_print.cpp52
-rw-r--r--test/test_file.cpp28
16 files changed, 470 insertions, 41 deletions
diff --git a/.gitignore b/.gitignore
index a7abdc1..2fd3d90 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
.cache
*build/
+debug/
*release/
.ccls-cache
compile_commands.json
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e40d7b2..465e35d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,10 +19,12 @@ add_library(fud SHARED
source/fud_allocator.cpp
source/fud_assert.cpp
source/fud_c_file.cpp
+ source/fud_csv.cpp
source/fud_directory.cpp
source/fud_file.cpp
source/fud_format.cpp
source/fud_memory.cpp
+ source/fud_print.cpp
source/fud_sqlite.cpp
source/fud_string_view.cpp
source/fud_string_convert.cpp
@@ -103,16 +105,19 @@ set(FUD_HEADERS
"include/fud_algorithm.hpp"
"include/fud_array.hpp"
"include/fud_assert.hpp"
+ "include/fud_csv.hpp"
"include/fud_c_file.hpp"
"include/fud_c_string.hpp"
"include/fud_directory.hpp"
"include/fud_drain.hpp"
"include/fud_file.hpp"
"include/fud_fixed_vector.hpp"
- "include/fud_fud_type_traits.hpp"
+ "include/fud_type_traits.hpp"
+ "include/fud_format.hpp"
"include/fud_memory.hpp"
"include/fud_option.hpp"
"include/fud_permissions.hpp"
+ "include/fud_print.hpp"
"include/fud_result.hpp"
"include/fud_span.hpp"
"include/fud_status.hpp"
@@ -120,6 +125,7 @@ set(FUD_HEADERS
"include/fud_string_convert.hpp"
"include/fud_string_view.hpp"
"include/fud_sqlite.hpp"
+ "include/fud_text.hpp"
"include/fud_unique_array.hpp"
"include/fud_utf8.hpp"
"include/fud_utf8_iterator.hpp"
diff --git a/README.org b/README.org
index c693d68..4fe05c9 100644
--- a/README.org
+++ b/README.org
@@ -28,3 +28,4 @@ user controlled allocators.
+ Unicode support with UTF8 encoding
+ Formatting à la =std::format=
+ Wrappers around C files
++ CSV parsing
diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake
index a2c5798..07c39b5 100644
--- a/cmake/warnings.cmake
+++ b/cmake/warnings.cmake
@@ -8,10 +8,6 @@ set(FUD_WARNINGS
# -pedantic-errors
-Wstack-usage=2048 # GCC specific
-Wvla # variable modified types don't play nice in C++
- # types
- -Wlong-long
- -Wlong-long
- # -Winline
# memory / data / array / string
-Wsizeof-pointer-memaccess
-Wpacked
@@ -81,7 +77,8 @@ set(FUD_WARNINGS
# this plays badly with clangd
# -Wzero-as-null-pointer-constant
-Wlogical-op
- -Wuseless-cast
+ # disabled because of dragonbox
+ # -Wuseless-cast
-Wextra-semi
-Wredundant-decls
-Wmisleading-indentation
diff --git a/include/fud_algorithm.hpp b/include/fud_algorithm.hpp
index 0ad71d5..01cc5d3 100644
--- a/include/fud_algorithm.hpp
+++ b/include/fud_algorithm.hpp
@@ -27,6 +27,20 @@
namespace fud {
+template<typename T>
+concept LessThanComparable =
+ requires(T lhs, T rhs) {
+ { lhs < rhs } -> std::convertible_to<bool>;
+};
+
+template <LessThanComparable T>
+inline const T& min(const T& lhs, const T& rhs) {
+ if (lhs < rhs) {
+ return lhs;
+ }
+ return rhs;
+}
+
template <std::integral T>
class Iota {
public:
diff --git a/include/fud_allocator.hpp b/include/fud_allocator.hpp
index 9b90deb..95e1d5a 100644
--- a/include/fud_allocator.hpp
+++ b/include/fud_allocator.hpp
@@ -26,6 +26,12 @@
namespace fud {
+/** \brief The default allocation function for globalFudAllocator. */
+extern std::byte* fudAlloc(size_t size);
+
+/** \brief The default deallocation function for globalFudAllocator. */
+extern void fudFree(std::byte* ptr);
+
class alignas(std::max_align_t) Allocator {
public:
virtual ~Allocator() = default;
@@ -68,11 +74,41 @@ class NullAllocator : public Allocator {
extern NullAllocator globalNullAllocator;
-/** \brief The default allocation function for globalFudAllocator. */
-extern std::byte* fudAlloc(size_t size);
-
-/** \brief The default deallocation function for globalFudAllocator. */
-extern void fudFree(std::byte* ptr);
+template <size_t Size>
+class SimpleStackAllocator final : public Allocator {
+private:
+ std::byte m_memory[Size]{};
+ size_t m_allocated{0};
+
+public:
+ virtual ~SimpleStackAllocator() override final = default;
+
+ virtual Result<std::byte*, FudStatus> allocate(size_t bytes, size_t alignment = alignof(std::max_align_t)) override final {
+ using RetType = Result<std::byte*, FudStatus>;
+ static_cast<void>(alignment);
+ if (bytes > Size - m_allocated) {
+ return RetType::error(FudStatus::AllocFailure);
+ }
+
+ auto* data = m_memory + m_allocated;
+ m_allocated += bytes;
+
+ return RetType::okay(data);
+ }
+
+ virtual void deallocate(std::byte* pointer, size_t bytes) override final
+ {
+ if (pointer + bytes != m_memory + m_allocated) {
+ m_allocated = Size;
+ return;
+ }
+ m_allocated -= bytes;
+ }
+
+ virtual bool isEqual(const Allocator& rhs) const override final {
+ return &rhs == this;
+ }
+};
} // namespace fud
diff --git a/include/fud_csv.hpp b/include/fud_csv.hpp
new file mode 100644
index 0000000..efd37e6
--- /dev/null
+++ b/include/fud_csv.hpp
@@ -0,0 +1,82 @@
+/*
+ * libfud
+ * Copyright 2025 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FUD_CSV_HPP
+#define FUD_CSV_HPP
+
+#include "fud_file.hpp"
+#include "fud_status.hpp"
+#include "fud_string_view.hpp"
+#include "fud_text.hpp"
+#include "fud_vector.hpp"
+
+#include <functional> // reference_wrapper
+
+namespace fud {
+
+using TextBuffer = Vector<std::byte>;
+using CsvBuffer = Vector<std::byte>;
+using CsvLine = Vector<StringView>;
+
+struct Csv {
+ /** \brief The number of lines of data in the CSV. */
+ size_t numLines;
+
+ /** \brief The number of columns in the CSV. */
+ size_t numColumns;
+
+ /** \brief Buffer for each line with numColumns of StringView. */
+ Vector<CsvLine> lines;
+
+ /** \brief Backing buffer for data. */
+ CsvBuffer buffer;
+
+ /** \separator for each column */
+ Utf8 columnDelimiter{Ascii{','}};
+
+ /** \separator for each line */
+ NewlineRepr newlineDelimiter{NewlineRepr::Posix};
+
+ bool strict;
+
+ /** \brief Uses global Fud allocator for lines and backing buffer. */
+ static Csv makeDefault();
+
+ /** \brief Specify allocator to use for both lines and backing buffer. */
+ static Csv makeSingleAllocator(Allocator& allocator);
+
+ /** \brief Specify allocator. */
+ static Csv make(Allocator& lineAllocator, Allocator& bufferAllocator);
+
+ /** Consume and return the CSV. */
+ static FudStatus parseCsvFromFilename(
+ Csv& csv,
+ Option<TextBuffer&&> bufferOption,
+ StringView filename,
+ OpenFlags flags = OpenFlags{},
+ Option<int> dirFdOption = NullOpt);
+
+ // assumes file is at start
+ static FudStatus parseCsvFromUnbufferedFile(Csv& csv, RegularFile&& file);
+
+ // assumes file is at start
+ static FudStatus parseCsvFromBufferedFile(Csv& csv, BufferedRegularFile& file);
+};
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_file.hpp b/include/fud_file.hpp
index 82f8291..e7c485c 100644
--- a/include/fud_file.hpp
+++ b/include/fud_file.hpp
@@ -197,6 +197,10 @@ class BufferedRegularFile {
FudStatus close(bool discardBuffer);
+ Result<size_t, FudStatus> size() const {
+ return m_file.size();
+ }
+
/** \brief Write from source to file as sink. */
DrainResult write(const std::byte* source, size_t length, Option<size_t> maxExtraAttempts);
@@ -217,6 +221,8 @@ class BufferedRegularFile {
FudStatus seek(size_t position);
+ Result<size_t, FudStatus> searchSubstring(StringView subString);
+
constexpr const RegularFile& file() const
{
return m_file;
diff --git a/include/fud_print.hpp b/include/fud_print.hpp
new file mode 100644
index 0000000..592b106
--- /dev/null
+++ b/include/fud_print.hpp
@@ -0,0 +1,40 @@
+/*
+ * libfud
+ * Copyright 2024 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FUD_PRINT_HPP
+#define FUD_PRINT_HPP
+
+#include "fud_format.hpp"
+
+namespace fud {
+
+struct StdOutSink {
+ DrainResult drain(StringView source);
+};
+
+FormatResult print(FormatString fmt);
+
+template <typename... Args>
+FormatResult print(FormatString fmt, Args&&... args)
+{
+ StdOutSink outSink{};
+ return format(outSink, FormatCharMode::Unchecked, fmt, std::forward<Args>(args)...);
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_text.hpp b/include/fud_text.hpp
new file mode 100644
index 0000000..683e911
--- /dev/null
+++ b/include/fud_text.hpp
@@ -0,0 +1,50 @@
+/*
+ * libfud
+ * Copyright 2025 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FUD_TEXT_HPP
+#define FUD_TEXT_HPP
+
+#include "fud_string_view.hpp"
+#include <cstdint>
+
+namespace fud {
+
+enum class NewlineRepr : uint8_t {
+ Posix, // \n
+ MSDOS, // \r\n
+ C64, // \r (Commodore 64)
+ RiscOS, // \n\r
+};
+
+constexpr StringView newlineText(NewlineRepr repr) {
+ switch (repr) {
+ case NewlineRepr::Posix:
+ return StringView{u8"\n"};
+ case NewlineRepr::MSDOS:
+ return StringView{u8"\r\n"};
+ case NewlineRepr::C64:
+ return StringView{u8"\r"};
+ case NewlineRepr::RiscOS:
+ return StringView{u8"\n\r"};
+ default:
+ return StringView{u8"\n"};
+ }
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/fud_fud_type_traits.hpp b/include/fud_type_traits.hpp
index 3fdff79..3fdff79 100644
--- a/include/fud_fud_type_traits.hpp
+++ b/include/fud_type_traits.hpp
diff --git a/source/fud_assert.cpp b/source/fud_assert.cpp
index 966f44c..425826d 100644
--- a/source/fud_assert.cpp
+++ b/source/fud_assert.cpp
@@ -17,8 +17,8 @@
#include "fud_assert.hpp"
-#include "fud_format.hpp"
#include "fud_drain.hpp"
+#include "fud_format.hpp"
#include "fud_string_view.hpp"
#include <cstdio>
@@ -26,11 +26,11 @@
namespace fud {
-struct BufferSink {
+struct StdErrSink {
DrainResult drain(StringView source);
};
-DrainResult BufferSink::drain(StringView source)
+DrainResult StdErrSink::drain(StringView source)
{
DrainResult result{0, FudStatus::Success};
if (source.m_data == nullptr) {
@@ -52,7 +52,7 @@ DrainResult BufferSink::drain(StringView source)
namespace impl {
void assertFailMessage(const char* assertion, const std::source_location sourceLocation)
{
- BufferSink sink;
+ StdErrSink sink;
const char* fileName = sourceLocation.file_name();
if (fileName == nullptr) {
fileName = "Unknown file";
diff --git a/source/fud_csv.cpp b/source/fud_csv.cpp
new file mode 100644
index 0000000..031fcbc
--- /dev/null
+++ b/source/fud_csv.cpp
@@ -0,0 +1,92 @@
+/*
+ * libfud
+ * Copyright 2025 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fud_csv.hpp"
+
+namespace fud {
+
+FudStatus Csv::parseCsvFromFilename(
+ Csv& csv,
+ Option<TextBuffer&&> bufferOption,
+ StringView filename,
+ OpenFlags flags,
+ Option<int> dirFdOption)
+{
+ auto fileResult{RegularFile::open(filename, FileAccessMode::Read, flags, dirFdOption)};
+
+ if (fileResult.isError()) {
+ return fileResult.takeError();
+ }
+
+ if (bufferOption.hasValue()) {
+ auto bufferedFile{BufferedRegularFile::make(fileResult.takeOkay(), std::move(bufferOption.value()))};
+ return parseCsvFromBufferedFile(csv, bufferedFile);
+ }
+
+ auto unbufferedFile{fileResult.takeOkay()};
+ return parseCsvFromUnbufferedFile(csv, std::move(unbufferedFile));
+}
+
+enum class CsvTextState : uint8_t
+{
+ UnquotedField,
+ QuotedField,
+ Separator,
+ Newline,
+};
+
+FudStatus Csv::parseCsvFromBufferedFile(Csv& csv, BufferedRegularFile& file)
+{
+ auto lineEnding{newlineText(csv.newlineDelimiter)};
+ static_cast<void>(lineEnding);
+ DrainResult readResult{};
+ while (true) {
+ utf8 letter{};
+ auto drainResult = file.read(reinterpret_cast<std::byte*>(&letter), sizeof(letter), NullOpt);
+ readResult.status = drainResult.status;
+ readResult.bytesDrained += drainResult.bytesDrained;
+ // if (status
+ // REMOVE
+ break;
+ }
+
+ size_t rawSize = 0;
+
+ while (true) {
+ rawSize++;
+ // REMOVE
+ break;
+ }
+
+ auto reserveStatus = csv.buffer.reserve(rawSize);
+ if (reserveStatus != FudStatus::Success) {
+ return reserveStatus;
+ }
+
+ return FudStatus::NotImplemented;
+}
+
+FudStatus Csv::parseCsvFromUnbufferedFile(Csv& csv, RegularFile&& file)
+{
+ static_cast<void>(csv);
+ constexpr size_t BufferSize = 256;
+ SimpleStackAllocator<BufferSize> stackAllocator{};
+ auto bufferedFile{BufferedRegularFile::make(std::move(file), TextBuffer{stackAllocator})};
+ return parseCsvFromBufferedFile(csv, bufferedFile);
+}
+
+} // namespace fud
diff --git a/source/fud_file.cpp b/source/fud_file.cpp
index 55f8dbc..caf0f5a 100644
--- a/source/fud_file.cpp
+++ b/source/fud_file.cpp
@@ -17,6 +17,8 @@
#include "fud_file.hpp"
+#include "fud_algorithm.hpp"
+
#include <cerrno>
#include <fcntl.h>
#include <linux/openat2.h>
@@ -625,6 +627,7 @@ DrainResult BufferedRegularFile::write(const std::byte* source, size_t length, O
DrainResult BufferedRegularFile::read(std::byte* sink, size_t length, Option<size_t> maxExtraAttempts)
{
+ auto extraAttempts = maxExtraAttempts.valueOr(0);
DrainResult result{0, FudStatus::Success};
if (sink == nullptr) {
@@ -637,15 +640,6 @@ DrainResult BufferedRegularFile::read(std::byte* sink, size_t length, Option<siz
return result;
}
- if (m_lastOperation != Operation::Write) {
- m_bufferLength = 0;
- m_lastOperation = Operation::Write;
- }
-
- if (length == 0) {
- return result;
- }
-
if (m_lastOperation == Operation::Write && m_bufferLength > 0) {
result.status = FudStatus::OperationInvalid;
return result;
@@ -653,13 +647,18 @@ DrainResult BufferedRegularFile::read(std::byte* sink, size_t length, Option<siz
if (m_lastOperation != Operation::Read) {
m_lastOperation = Operation::Read;
+ m_bufferPosition = 0;
+ m_bufferLength = 0;
+ }
+
+ if (length == 0) {
+ return result;
}
if (m_bufferLength > 0 && m_bufferPosition < m_bufferLength) {
- auto count = m_bufferLength - m_bufferPosition;
- if (count > length) {
- count = length;
- }
+ auto remainingLength = m_bufferLength - m_bufferPosition;
+ auto count = min(length, remainingLength);
+
auto copyStatus = copyMem(sink, length, m_buffer.data() + m_bufferPosition, count);
fudAssert(copyStatus == FudStatus::Success);
@@ -677,23 +676,52 @@ DrainResult BufferedRegularFile::read(std::byte* sink, size_t length, Option<siz
m_bufferLength = 0;
}
+ if (length == 0) {
+ return result;
+ }
+
+ fudAssert(m_bufferLength == 0);
+ fudAssert(m_bufferPosition == 0);
+
if (length > m_buffer.size()) {
- auto drainResult = m_file.read(sink, length, maxExtraAttempts.valueOr(0));
+ auto drainResult = m_file.read(sink, length, extraAttempts);
+
+ sink += drainResult.bytesDrained;
+ length -= drainResult.bytesDrained;
+
result.status = drainResult.status;
result.bytesDrained += drainResult.bytesDrained;
+ return result;
}
- if (length == 0 || result.status != FudStatus::Success) {
+ fudAssert(length > 0);
+
+ if (result.status != FudStatus::Success) {
return result;
}
- fudAssert(m_bufferLength == 0 && m_bufferPosition == 0);
- auto drainResult = m_file.read(m_buffer.data(), m_buffer.size(), maxExtraAttempts.valueOr(0));
- if (drainResult.status != FudStatus::Success && drainResult.status != FudStatus::Partial) {
- result.status = drainResult.status;
- } else {
- result.status = FudStatus::Success;
+ fudAssert(m_bufferLength == 0);
+ fudAssert(m_bufferPosition == 0);
+
+ auto drainResult = m_file.read(m_buffer.data(), m_buffer.size(), extraAttempts);
+ result.status = drainResult.status;
+ if (drainResult.status == FudStatus::Success || drainResult.status == FudStatus::Partial) {
m_bufferLength = drainResult.bytesDrained;
+
+ auto count = min(length, m_bufferLength);
+
+ auto copyStatus = copyMem(sink, count, m_buffer.data(), count);
+ fudAssert(copyStatus == FudStatus::Success);
+
+ sink += count;
+ length -= count;
+
+ if (drainResult.status == FudStatus::Partial && length == 0) {
+ drainResult.status = FudStatus::Success;
+ }
+
+ m_bufferPosition = count;
+ result.bytesDrained += count;
}
return result;
@@ -713,6 +741,12 @@ DrainResult BufferedRegularFile::flush(size_t maxExtraAttempts)
return {0, FudStatus::Success};
}
+ if (m_lastOperation != Operation::Write) {
+ m_bufferLength = 0;
+ m_bufferPosition = 0;
+ return {0, FudStatus::Success};
+ }
+
auto drainResult = m_file.write(m_buffer.data(), m_bufferLength, maxExtraAttempts);
if (drainResult.status == FudStatus::Success) {
m_bufferLength = 0;
diff --git a/source/fud_print.cpp b/source/fud_print.cpp
new file mode 100644
index 0000000..85a3d76
--- /dev/null
+++ b/source/fud_print.cpp
@@ -0,0 +1,52 @@
+/*
+ * libfud
+ * Copyright 2024 Dominick Allen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fud_print.hpp"
+#include "fud_drain.hpp"
+#include "fud_string_view.hpp"
+
+#include <cstdio>
+#include <exception>
+
+namespace fud {
+
+DrainResult StdOutSink::drain(StringView source)
+{
+ DrainResult result{0, FudStatus::Success};
+ if (source.m_data == nullptr) {
+ result.status = FudStatus::NullPointer;
+ return result;
+ }
+ if (source.m_length == 0) {
+ result.status = FudStatus::Success;
+ return result;
+ }
+ /* TODO: give users control over this functionality */
+ result.bytesDrained = fwrite(reinterpret_cast<const char*>(source.m_data), 1, source.m_length, stdout);
+ if (result.bytesDrained != source.m_length) {
+ result.status = FudStatus::Full;
+ }
+ return result;
+}
+
+FormatResult print(FormatString fmt)
+{
+ StdOutSink outSink{};
+ return format(outSink, FormatCharMode::Unchecked, fmt);
+}
+
+} // namespace fud
diff --git a/test/test_file.cpp b/test/test_file.cpp
index 62df1ae..9727e94 100644
--- a/test/test_file.cpp
+++ b/test/test_file.cpp
@@ -143,15 +143,33 @@ TEST(FudBufferedFile, OpenReadWrite)
ASSERT_EQ(bufferedFile.seekStart(), FudStatus::Success);
Vector<utf8> output{Vector<utf8>::withSize(testName.size()).takeOkay()};
- auto readResult = bufferedFile.read(
- reinterpret_cast<std::byte*>(output.data()),
- testName.size(),
- testName.size());
+ auto readResult = bufferedFile.read(reinterpret_cast<std::byte*>(output.data()), testName.size(), NullOpt);
ASSERT_EQ(readResult, expected);
-
+
EXPECT_EQ(output.size(), testName.size());
EXPECT_EQ(0, compareMem(output.data(), output.size(), testName.data(), testName.size()).takeOkayOr(-1));
+ ASSERT_EQ(bufferedFile.flush(), nullExpected);
+ ASSERT_EQ(bufferedFile.seekStart(), FudStatus::Success);
+
+ expected.bytesDrained = 1;
+ readResult = bufferedFile.read(reinterpret_cast<std::byte*>(output.data()), 1, NullOpt);
+ ASSERT_EQ(readResult, expected);
+ EXPECT_EQ(output[0], testName.data()[0]);
+
+ expected.bytesDrained = testName.size() - 2;
+ readResult = bufferedFile.read(reinterpret_cast<std::byte*>(output.data()) + 1, testName.size() - 2, NullOpt);
+ ASSERT_EQ(readResult, expected);
+ EXPECT_EQ(
+ 0,
+ compareMem(output.data() + 1, output.size() - 1, testName.data() + 1, testName.size() - 2).takeOkayOr(-1));
+
+ expected.bytesDrained = 1;
+ readResult = bufferedFile.read(reinterpret_cast<std::byte*>(output.data()), 1, NullOpt);
+
+ EXPECT_TRUE(readResult.status == FudStatus::Success || readResult.status == FudStatus::Partial);
+ EXPECT_EQ(output[testName.size() - 1], testName.data()[testName.size() - 1]);
+
EXPECT_EQ(bufferedFile.close(true), FudStatus::Success);
ASSERT_EQ(rmFile(testName), 0);
}