/* * 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_FILE_HPP #define FUD_FILE_HPP #include "fud_drain.hpp" #include "fud_option.hpp" #include "fud_permissions.hpp" #include "fud_result.hpp" #include "fud_status.hpp" #include "fud_string_view.hpp" #include "fud_vector.hpp" #include namespace fud { /** \brief Access Modes for File */ enum class FileAccessMode : uint8_t { Read = 0x01, Write = 0x02, ReadWrite = Read | Write }; enum class OpenFlagEnum : uint8_t { Append = 0x01, Truncate = Append << 1, CloseOnExec = Truncate << 1, DataSync = CloseOnExec << 1, Direct = DataSync << 1, NoAtime = Direct << 1, NonBlock = NoAtime << 1, FileSync = NonBlock << 1 }; class OpenFlags { public: using FlagType = std::underlying_type_t; constexpr OpenFlags() noexcept = default; constexpr OpenFlags(const OpenFlags& rhs) noexcept = default; constexpr OpenFlags(OpenFlags&& rhs) noexcept = default; constexpr ~OpenFlags() noexcept = default; constexpr OpenFlags& operator=(const OpenFlags& rhs) noexcept = default; constexpr OpenFlags& operator=(OpenFlags&& rhs) noexcept = default; constexpr OpenFlags(OpenFlagEnum mode) noexcept : m_mask{static_cast(mode)} { } constexpr OpenFlags operator|(OpenFlags rhs) const noexcept { OpenFlags mode{*this}; mode.m_mask |= rhs.m_mask; return mode; } constexpr OpenFlags& operator|=(OpenFlags rhs) noexcept { m_mask |= rhs.m_mask; return *this; } constexpr OpenFlags& operator|=(OpenFlagEnum rhs) noexcept { m_mask |= static_cast(rhs); return *this; } constexpr OpenFlags operator|(OpenFlagEnum rhs) const noexcept { OpenFlags mode{*this}; mode.m_mask |= static_cast(rhs); return mode; } [[nodiscard]] constexpr uint32_t flags() const noexcept { uint32_t openFlags = 0; openFlags |= static_cast(hasFlag(OpenFlagEnum::Append)) * O_APPEND; openFlags |= static_cast(hasFlag(OpenFlagEnum::Truncate)) * O_TRUNC; openFlags |= static_cast(hasFlag(OpenFlagEnum::CloseOnExec)) * O_CLOEXEC; openFlags |= static_cast(hasFlag(OpenFlagEnum::DataSync)) * O_DSYNC; openFlags |= static_cast(hasFlag(OpenFlagEnum::Direct)) * O_DIRECT; openFlags |= static_cast(hasFlag(OpenFlagEnum::NoAtime)) * O_NOATIME; openFlags |= static_cast(hasFlag(OpenFlagEnum::NonBlock)) * O_NONBLOCK; openFlags |= static_cast(hasFlag(OpenFlagEnum::FileSync)) * O_SYNC; return openFlags; } [[nodiscard]] constexpr bool hasFlag(OpenFlagEnum flag) const noexcept { return (m_mask & static_cast(flag)) != 0; } private: FlagType m_mask{0}; }; constexpr OpenFlags operator|(OpenFlagEnum lhs, OpenFlagEnum rhs) { OpenFlags mode{lhs}; return mode | rhs; } class RegularFile; using FileResult = Result; class RegularFile { public: friend class BufferedRegularFile; static FileResult open(StringView filename, FileAccessMode mode, OpenFlags flags, Option dirFdoption); static FileResult create( StringView filename, FileAccessMode mode, OpenFlags flags, Permissions permissions, bool createOnly, Option dirFdOption); FudStatus close(); FudStatus take(RegularFile& rhs); Result size() const; [[nodiscard]] constexpr int fileDescriptor() const { return m_fd; } [[nodiscard]] constexpr bool isOpen() const { return m_fd >= 0; } FudStatus seekStart(); FudStatus seekEnd(); FudStatus seek(size_t position); [[nodiscard]] constexpr size_t position() const { return m_position; } /** \brief Write from source to file as sink. */ DrainResult write(const std::byte* source, size_t length, size_t maxExtraAttempts = 0); DrainResult read(std::byte* sink, size_t length, size_t maxExtraAttempts = 0); private: constexpr RegularFile() = default; FudStatus validateIOParameters(const std::byte* source) const; public: RegularFile(const RegularFile& rhs) = delete; constexpr RegularFile(RegularFile&& rhs) noexcept : m_position{rhs.m_position}, m_fd{rhs.m_fd}, m_openFlags{rhs.m_openFlags}, m_modeFlags{rhs.m_modeFlags} { rhs.m_fd = -1; } ~RegularFile(); RegularFile& operator=(const RegularFile& rhs) = delete; RegularFile& operator=(RegularFile&& rhs) noexcept; private: size_t m_position{0}; int m_fd{-1}; OpenFlags m_openFlags{}; FileAccessMode m_modeFlags{}; }; class BufferedRegularFile { public: static BufferedRegularFile make(RegularFile&& file, Vector&& buffer = Vector::NullVector()); FudStatus close(bool discardBuffer); Result size() const { return m_file.size(); } /** \brief Write from source to file as sink. */ DrainResult write(const std::byte* source, size_t length, Option maxExtraAttempts); /** \brief Read from file as source to sink. */ DrainResult read(std::byte* sink, size_t length, Option maxExtraAttempts); /** \brief Attempt to read one UTF8 sequence. */ DrainResult readUtf8(Utf8& sink, Option maxExtraAttempts); FudStatus setBuffer(Vector&& buffer, bool discardOldBuffer); DrainResult flush(size_t maxExtraAttempts = 0); void discard(); FudStatus resizeBuffer(size_t size); FudStatus seekStart(); FudStatus seekEnd(); FudStatus seek(size_t position); Result searchSubstring(StringView subString); [[nodiscard]] constexpr const RegularFile& file() const { return m_file; } constexpr RegularFile& file() { return m_file; } [[nodiscard]] constexpr bool bufferEmpty() const { return m_bufferLength == 0; } private: constexpr BufferedRegularFile() noexcept = default; constexpr BufferedRegularFile(RegularFile&& regularFile, Vector&& buffer) noexcept : m_buffer{std::move(buffer)}, m_file{std::move(regularFile)} { } Vector m_buffer{Vector::NullVector()}; RegularFile m_file; size_t m_bufferLength{0}; size_t m_bufferPosition{0}; enum class Operation : uint8_t { None, Write, Read }; Operation m_lastOperation{Operation::None}; DrainResult validateBufferedIO(const std::byte* pointer, Operation requestedOperation); void drainReadBuffer(std::byte*& sink, size_t& length, DrainResult& result); }; } // namespace fud #endif