<commit_before>#pragma once

#include <config.h>

#include "Logging.hpp"
#include "NonCopyable.hpp"
#include "PageManager.hpp"

#include <atomic>
#include <cstddef>
#include <iterator>

namespace tell {
namespace store {

/**
 * @brief A single entry in the log able to store any arbitrary data
 */
class LogEntry : NonCopyable, NonMovable {
public:
    /// Size of the LogEntry data structure
    static constexpr size_t LOG_ENTRY_SIZE = 4;

    /**
     * @brief Returns the LogEntry struct associated with a given data pointer
     *
     * The data pointer has to be the pointer that was obtained from LogEntry::data().
     *
     * @param data The data pointer
     * @return The LogEntry struct associated with the data pointer
     */
    static LogEntry* entryFromData(char* data) {
        return const_cast<LogEntry*>(entryFromData(const_cast<const char*>(data)));
    }

    static const LogEntry* entryFromData(const char* data) {
        return reinterpret_cast<const LogEntry*>(data - LOG_ENTRY_SIZE);
    }

    /**
     * @brief Constructor will never be called
     *
     * Everything will be zero initialized.
     */
    LogEntry() = delete;

    /**
     * @brief Destructor will never be called
     */
    ~LogEntry() = delete;

    /**
     * @brief The size of the entry in the log
     *
     * @note This is not the size of the pure data payload but the size of the whole (8 byte padded) log entry.
     */
    uint32_t size() const {
        return (mSize.load() & (0xFFFFFFFFu << 1));
    }

    char* data() {
        return reinterpret_cast<char*>(this) + LOG_ENTRY_SIZE;
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + LOG_ENTRY_SIZE;
    }

    /**
     * @brief Whether the entry was written completely
     *
     * Checks if the LSB of size is 0.
     */
    bool sealed() const {
        return ((mSize.load() & 0x1u) == 0);
    }

    /**
     * @brief Seals the entry indicating that it has been completely written
     *
     * Sets the LSB of size to 0.
     */
    void seal() {
        LOG_ASSERT(!sealed(), "Entry is already sealed");
        mSize.fetch_and(0xFFFFFFFFu << 1);
    }

private:
    friend class LogPage;

    /**
     * @brief Tries to create a new LogEntry at this log position
     *
     * Acquires the space for this entry in the log by compare and swapping the size field from 0 to the given size.
     * Because the PageManager returns zeroed pages, the object is zero initalized (and size thus 0) if nobody has
     * written to it yet.
     *
     * @param size Size of the whole 8 byte padded log entry
     * @return 0 if the log entry was successfully acquired else the previously reserved size of this log entry
     */
    uint32_t tryAcquire(uint32_t size);

    /// Size of this LogEntry as the 8 byte aligned size of the LogEntry class plus data payload
    std::atomic<uint32_t> mSize;
};

/**
 * @brief A single page in the log containing multiple LogEntry's
 *
 * A Log-Page has the following form:
 *
 * ---------------------------------------------------------------------------------------------------
 * | next (8 bytes) | offset (4 bytes) | padding (8 bytes) | entry | entry | ... | padding (4 bytes) |
 * ---------------------------------------------------------------------------------------------------
 *
 * Entries require 4 bytes of space followed by the associated data segment. To keep this data segment 8 byte aligned
 * the log pads the entries to a multiple of 8 bytes and writes the LogEntries at offset 4. Any subsequent entries are
 * aligned with offset 4 due to the padding.
 */
class LogPage : NonCopyable, NonMovable {
public:
    /// Size of the LogPage data structure
    static constexpr size_t LOG_HEADER_SIZE = 20;

    /// Maximum size of a log entry
    static constexpr uint32_t MAX_ENTRY_SIZE = TELL_PAGE_SIZE - (LogPage::LOG_HEADER_SIZE + 4);

    /// Maximum size of a log entries data payload
    static constexpr uint32_t MAX_DATA_SIZE = MAX_ENTRY_SIZE - LogEntry::LOG_ENTRY_SIZE;

    /**
     * @brief Iterator for iterating over all entries in a page
     */
    class EntryIterator : public std::iterator<std::input_iterator_tag, LogEntry> {
    public:
        EntryIterator(LogPage* page, uint32_t pos)
                : mPage(page),
                  mPos(pos) {
        }

        LogPage* page() const {
            return mPage;
        }

        uint32_t offset() const {
            return mPos;
        }

        EntryIterator& operator++() {
            auto entry = reinterpret_cast<LogEntry*>(mPage->data() + mPos);
            mPos += entry->size();
            return *this;
        }

        EntryIterator operator++(int) {
            EntryIterator result(*this);
            operator++();
            return result;
        }

        bool operator==(const EntryIterator& rhs) const {
            return ((mPage == rhs.mPage) && (mPos == rhs.mPos));
        }

        bool operator!=(const EntryIterator& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        pointer operator->() const {
            return reinterpret_cast<LogEntry*>(mPage->data() + mPos);
        }

    private:
        /// Page this iterator belongs to
        LogPage* mPage;

        /// Current offset the iterator is pointing to
        uint32_t mPos;
    };

    LogPage()
            : mOffset(0x1u) {
    }

    char* data() {
        return reinterpret_cast<char*>(this) + LOG_HEADER_SIZE;
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + LOG_HEADER_SIZE;
    }<commit_msg>Call const version of data in Log for consistency<commit_after>#pragma once

#include <config.h>

#include "Logging.hpp"
#include "NonCopyable.hpp"
#include "PageManager.hpp"

#include <atomic>
#include <cstddef>
#include <iterator>

namespace tell {
namespace store {

/**
 * @brief A single entry in the log able to store any arbitrary data
 */
class LogEntry : NonCopyable, NonMovable {
public:
    /// Size of the LogEntry data structure
    static constexpr size_t LOG_ENTRY_SIZE = 4;

    /**
     * @brief Returns the LogEntry struct associated with a given data pointer
     *
     * The data pointer has to be the pointer that was obtained from LogEntry::data().
     *
     * @param data The data pointer
     * @return The LogEntry struct associated with the data pointer
     */
    static LogEntry* entryFromData(char* data) {
        return const_cast<LogEntry*>(entryFromData(const_cast<const char*>(data)));
    }

    static const LogEntry* entryFromData(const char* data) {
        return reinterpret_cast<const LogEntry*>(data - LOG_ENTRY_SIZE);
    }

    /**
     * @brief Constructor will never be called
     *
     * Everything will be zero initialized.
     */
    LogEntry() = delete;

    /**
     * @brief Destructor will never be called
     */
    ~LogEntry() = delete;

    /**
     * @brief The size of the entry in the log
     *
     * @note This is not the size of the pure data payload but the size of the whole (8 byte padded) log entry.
     */
    uint32_t size() const {
        return (mSize.load() & (0xFFFFFFFFu << 1));
    }

    char* data() {
        return const_cast<char*>(const_cast<const LogEntry*>(this)->data());
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + LOG_ENTRY_SIZE;
    }

    /**
     * @brief Whether the entry was written completely
     *
     * Checks if the LSB of size is 0.
     */
    bool sealed() const {
        return ((mSize.load() & 0x1u) == 0);
    }

    /**
     * @brief Seals the entry indicating that it has been completely written
     *
     * Sets the LSB of size to 0.
     */
    void seal() {
        LOG_ASSERT(!sealed(), "Entry is already sealed");
        mSize.fetch_and(0xFFFFFFFFu << 1);
    }

private:
    friend class LogPage;

    /**
     * @brief Tries to create a new LogEntry at this log position
     *
     * Acquires the space for this entry in the log by compare and swapping the size field from 0 to the given size.
     * Because the PageManager returns zeroed pages, the object is zero initalized (and size thus 0) if nobody has
     * written to it yet.
     *
     * @param size Size of the whole 8 byte padded log entry
     * @return 0 if the log entry was successfully acquired else the previously reserved size of this log entry
     */
    uint32_t tryAcquire(uint32_t size);

    /// Size of this LogEntry as the 8 byte aligned size of the LogEntry class plus data payload
    std::atomic<uint32_t> mSize;
};

/**
 * @brief A single page in the log containing multiple LogEntry's
 *
 * A Log-Page has the following form:
 *
 * ---------------------------------------------------------------------------------------------------
 * | next (8 bytes) | offset (4 bytes) | padding (8 bytes) | entry | entry | ... | padding (4 bytes) |
 * ---------------------------------------------------------------------------------------------------
 *
 * Entries require 4 bytes of space followed by the associated data segment. To keep this data segment 8 byte aligned
 * the log pads the entries to a multiple of 8 bytes and writes the LogEntries at offset 4. Any subsequent entries are
 * aligned with offset 4 due to the padding.
 */
class LogPage : NonCopyable, NonMovable {
public:
    /// Size of the LogPage data structure
    static constexpr size_t LOG_HEADER_SIZE = 20;

    /// Maximum size of a log entry
    static constexpr uint32_t MAX_ENTRY_SIZE = TELL_PAGE_SIZE - (LogPage::LOG_HEADER_SIZE + 4);

    /// Maximum size of a log entries data payload
    static constexpr uint32_t MAX_DATA_SIZE = MAX_ENTRY_SIZE - LogEntry::LOG_ENTRY_SIZE;

    /**
     * @brief Iterator for iterating over all entries in a page
     */
    class EntryIterator : public std::iterator<std::input_iterator_tag, LogEntry> {
    public:
        EntryIterator(LogPage* page, uint32_t pos)
                : mPage(page),
                  mPos(pos) {
        }

        LogPage* page() const {
            return mPage;
        }

        uint32_t offset() const {
            return mPos;
        }

        EntryIterator& operator++() {
            auto entry = reinterpret_cast<LogEntry*>(mPage->data() + mPos);
            mPos += entry->size();
            return *this;
        }

        EntryIterator operator++(int) {
            EntryIterator result(*this);
            operator++();
            return result;
        }

        bool operator==(const EntryIterator& rhs) const {
            return ((mPage == rhs.mPage) && (mPos == rhs.mPos));
        }

        bool operator!=(const EntryIterator& rhs) const {
            return !operator==(rhs);
        }

        reference operator*() {
            return *operator->();
        }

        pointer operator->() const {
            return reinterpret_cast<LogEntry*>(mPage->data() + mPos);
        }

    private:
        /// Page this iterator belongs to
        LogPage* mPage;

        /// Current offset the iterator is pointing to
        uint32_t mPos;
    };

    LogPage()
            : mOffset(0x1u) {
    }

    char* data() {
        return const_cast<char*>(const_cast<const LogPage*>(this)->data());
    }

    const char* data() const {
        return reinterpret_cast<const char*>(this) + LOG_HEADER_SIZE;
    }
{'fraction_non_alphanumeric': 0.12839852657428522, 'fraction_numerical': 0.008419575513067884, 'mean_word_length': 2.0793950850661624, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4958005', 'n_tokens_mistral': 3221, 'n_tokens_neox': 3079, 'n_words': 1140}