<commit_before>/*
 * Copyright (c) 2015 <NAME>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef AMBROLIB_FAT_FS_H
#define AMBROLIB_FAT_FS_H

#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include <aprinter/meta/MinMax.h>
#include <aprinter/meta/ChooseInt.h>
#include <aprinter/meta/EnableIf.h>
#include <aprinter/meta/FunctionIf.h>
#include <aprinter/meta/StructIf.h>
#include <aprinter/meta/If.h>
#include <aprinter/base/Object.h>
#include <aprinter/base/DebugObject.h>
#include <aprinter/base/Callback.h>
#include <aprinter/base/Assert.h>
#include <aprinter/base/BinaryTools.h>
#include <aprinter/base/WrapBuffer.h>
#include <aprinter/misc/Utf8Encoder.h>
#include <aprinter/misc/AsciiTools.h>
#include <aprinter/structure/DoubleEndedList.h>
#include <aprinter/fs/BlockCache.h>
#include <aprinter/fs/BlockRange.h>

#include <aprinter/BeginNamespace.h>

template <typename Context, typename ParentObject, typename TheBlockAccess, typename InitHandler, typename WriteMountHandler, typename Params>
class FatFs {
public:
    struct Object;
    static bool const FsWritable = Params::Writable;
    
private:
    using TheDebugObject = DebugObject<Context, Object>;
    using TheBlockCache = BlockCache<Context, Object, TheBlockAccess, Params::NumCacheEntries, FsWritable>;
    
    using BlockAccessUser = typename TheBlockAccess::User;
    using BlockIndexType = typename TheBlockAccess::BlockIndexType;
    static size_t const BlockSize = TheBlockAccess::BlockSize;
    using CacheBlockRef = typename TheBlockCache::CacheRef;
    using CacheFlushRequest = typename TheBlockCache::template FlushRequest<>;
    
    static_assert(BlockSize >= 0x47, "BlockSize not enough for EBPB");
    static_assert(BlockSize % 32 == 0, "BlockSize not a multiple of 32");
    static_assert(BlockSize >= 512, "BlockSize not enough for FS Information Sector");
    using ClusterIndexType = uint32_t;
    using ClusterBlockIndexType = uint16_t;
    static size_t const FatEntriesPerBlock = BlockSize / 4;
    static size_t const DirEntriesPerBlock = BlockSize / 32;
    using DirEntriesPerBlockType = ChooseIntForMax<DirEntriesPerBlock, false>;
    static_assert(Params::MaxFileNameSize >= 12, "");
    using FileNameLenType = ChooseIntForMax<Params::MaxFileNameSize, false>;
    static_assert(Params::NumCacheEntries >= 1, "");
    static size_t const EbpbStatusBitsOffset = 0x41;
    static uint8_t const StatusBitsDirty = 0x01;
    static ClusterIndexType const EndOfChainMarker = UINT32_C(0x0FFFFFFF);
    static ClusterIndexType const FreeClusterMarker = UINT32_C(0x00000000);
    static ClusterIndexType const EmptyFileMarker = UINT32_C(0x00000000);
    static ClusterIndexType const NormalClusterIndexEnd = UINT32_C(0x0FFFFFF8);
    static size_t const DirEntrySizeOffset = 0x1C;
    static size_t const FsInfoSig1Offset = 0x0;
    static size_t const FsInfoSig2Offset = 0x1E4;
    static size_t const FsInfoFreeClustersOffset = 0x1E8;
    static size_t const FsInfoAllocatedClusterOffset = 0x1EC;
    static size_t const FsInfoSig3Offset = 0x1FC;
    
    enum class FsState : uint8_t {INIT, READY, FAILED};
    enum class WriteMountState : uint8_t {NOT_MOUNTED, MOUNT_META, MOUNT_FSINFO, MOUNT_FLUSH, MOUNTED, UMOUNT_FLUSH1, UMOUNT_META, UMOUNT_FLUSH2};
    enum class AllocationState : uint8_t {IDLE, CHECK_EVENT, REQUESTING_BLOCK};
    
    template <bool Writable> class ClusterChain;
    template <bool Writable> class DirEntryRef;
    class DirectoryIterator;
    template <bool Writable> class WriteReference;
    
    APRINTER_STRUCT_IF_TEMPLATE(FsEntryExtra) {
        BlockIndexType dir_entry_block_index;
        DirEntriesPerBlockType dir_entry_block_offset;
    };
    
public:
    static size_t const TheBlockSize = BlockSize;
    
    enum class EntryType : uint8_t {DIR_TYPE, FILE_TYPE};
    
    class FsEntry : private FsEntryExtra<FsWritable> {
        friend FatFs;
        
    public:
        inline EntryType getType () const { return type; }
        inline uint32_t getFileSize () const { return file_size; }
        
    private:
        EntryType type;
        uint32_t file_size;
        ClusterIndexType cluster_index;
    };
    
    static bool isPartitionTypeSupported (uint8_t type)
    {
        return (type == 0xB || type == 0xC);
    }
    
    static void init (Context c, BlockRange<BlockIndexType> block_range)
    {
        auto *o = Object::self(c);
        
        TheBlockCache::init(c);
        
        o->block_range = block_range;
        o->state = FsState::INIT;
        
        o->init_block_ref.init(c, APRINTER_CB_STATFUNC_T(&FatFs::init_block_ref_handler));
        o->init_block_ref.requestBlock(c, get_abs_block_index(c, 0), 0, 1, true);
        
        fs_writable_init(c);
        
        TheDebugObject::init(c);
    }
    
    static void deinit (Context c)
    {
        auto *o = Object::self(c);
        TheDebugObject::deinit(c);
        
        fs_writable_deinit(c);
        
        if (o->state == FsState::INIT) {
            o->init_block_ref.deinit(c);
        }
        
        TheBlockCache::deinit(c);
    }
    
    static FsEntry getRootEntry (Context c)
    {
        auto *o = Object::self(c);
        TheDebugObject::access(c);
        AMBRO_ASSERT(o->state == FsState::READY)
        
        FsEntry entry;
        entry.type = EntryType::DIR_TYPE;
        entry.file_size = 0;
        entry.cluster_index = o->root_cluster;
        set_fs_entry_extra(&entry, 0, 0);
        return entry;
    }
    
    APRINTER_FUNCTION_IF_EXT(FsWritable, static, void, startWriteMount (Context c))
    {
        auto *o = Object::self(c);
        TheDebugObject::access(c);
        AMBRO_ASSERT(o->state == FsState::READY)
        AMBRO_ASSERT(o->write_mount_state == WriteMountState::NOT_MOUNTED)
        // implies
        AMBRO_ASSERT(o->num_write_references == 0)
        AMBRO_ASSERT(o->alloc_state == AllocationState::IDLE)
        
        o->write_mount_state = WriteMountState::MOUNT_META;
        o->write_block_ref.requestBlock(c, get_abs_block_index(c, 0), 0, 1, true);
    }
    
    APRINTER_FUNCTION_IF_EXT(FsWritable, static, bool, canStartWriteUnmount (Context c))
    {
        auto *o = Object::self(c);
        TheDebugObject::access(c);
        AMBRO_ASSERT(o->state == FsState::READY)
        AMBRO_ASSERT(o->write_mount_state == WriteMountState::MOUNTED)
        
        return (o->num_write_references == 0);
    }
    
    APRINTER_FUNCTION_IF_EXT(FsWritable, static, void, startWriteUnmount (Context c))
    {
        auto *o = Object::self(c);
        TheDebugObject::access(c);
        AMBRO_ASSERT(o->state == FsState::READY)
        AMBRO_ASSERT(o->write_mount_state == WriteMountState::MOUNTED)
        AMBRO_ASSERT(o->num_write_references == 0)
        // implies
        AMBRO_ASSERT(o->alloc_state == AllocationState::IDLE)
        
        o->write_mount_state = WriteMountState::UMOUNT_FLUSH1;
        o->flush_request.requestFlush(c);
    }
    
    class DirLister {
    public:
        using DirListerHandler = Callback<void(Context c, bool is_error, char const *name, FsEntry entry)>;
        
        void init (Context c, FsEntry dir_entry, DirListerHandler handler)<commit_msg>FatFs: Remove unused function.<commit_after>/*
 * Copyright (c) 2015 <NAME>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef AMBROLIB_FAT_FS_H
#define AMBROLIB_FAT_FS_H

#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include <aprinter/meta/MinMax.h>
#include <aprinter/meta/ChooseInt.h>
#include <aprinter/meta/EnableIf.h>
#include <aprinter/meta/FunctionIf.h>
#include <aprinter/meta/StructIf.h>
#include <aprinter/meta/If.h>
#include <aprinter/base/Object.h>
#include <aprinter/base/DebugObject.h>
#include <aprinter/base/Callback.h>
#include <aprinter/base/Assert.h>
#include <aprinter/base/BinaryTools.h>
#include <aprinter/base/WrapBuffer.h>
#include <aprinter/misc/Utf8Encoder.h>
#include <aprinter/misc/AsciiTools.h>
#include <aprinter/structure/DoubleEndedList.h>
#include <aprinter/fs/BlockCache.h>
#include <aprinter/fs/BlockRange.h>

#include <aprinter/BeginNamespace.h>

template <typename Context, typename ParentObject, typename TheBlockAccess, typename InitHandler, typename WriteMountHandler, typename Params>
class FatFs {
public:
    struct Object;
    static bool const FsWritable = Params::Writable;
    
private:
    using TheDebugObject = DebugObject<Context, Object>;
    using TheBlockCache = BlockCache<Context, Object, TheBlockAccess, Params::NumCacheEntries, FsWritable>;
    
    using BlockAccessUser = typename TheBlockAccess::User;
    using BlockIndexType = typename TheBlockAccess::BlockIndexType;
    static size_t const BlockSize = TheBlockAccess::BlockSize;
    using CacheBlockRef = typename TheBlockCache::CacheRef;
    using CacheFlushRequest = typename TheBlockCache::template FlushRequest<>;
    
    static_assert(BlockSize >= 0x47, "BlockSize not enough for EBPB");
    static_assert(BlockSize % 32 == 0, "BlockSize not a multiple of 32");
    static_assert(BlockSize >= 512, "BlockSize not enough for FS Information Sector");
    using ClusterIndexType = uint32_t;
    using ClusterBlockIndexType = uint16_t;
    static size_t const FatEntriesPerBlock = BlockSize / 4;
    static size_t const DirEntriesPerBlock = BlockSize / 32;
    using DirEntriesPerBlockType = ChooseIntForMax<DirEntriesPerBlock, false>;
    static_assert(Params::MaxFileNameSize >= 12, "");
    using FileNameLenType = ChooseIntForMax<Params::MaxFileNameSize, false>;
    static_assert(Params::NumCacheEntries >= 1, "");
    static size_t const EbpbStatusBitsOffset = 0x41;
    static uint8_t const StatusBitsDirty = 0x01;
    static ClusterIndexType const EndOfChainMarker = UINT32_C(0x0FFFFFFF);
    static ClusterIndexType const FreeClusterMarker = UINT32_C(0x00000000);
    static ClusterIndexType const EmptyFileMarker = UINT32_C(0x00000000);
    static ClusterIndexType const NormalClusterIndexEnd = UINT32_C(0x0FFFFFF8);
    static size_t const DirEntrySizeOffset = 0x1C;
    static size_t const FsInfoSig1Offset = 0x0;
    static size_t const FsInfoSig2Offset = 0x1E4;
    static size_t const FsInfoFreeClustersOffset = 0x1E8;
    static size_t const FsInfoAllocatedClusterOffset = 0x1EC;
    static size_t const FsInfoSig3Offset = 0x1FC;
    
    enum class FsState : uint8_t {INIT, READY, FAILED};
    enum class WriteMountState : uint8_t {NOT_MOUNTED, MOUNT_META, MOUNT_FSINFO, MOUNT_FLUSH, MOUNTED, UMOUNT_FLUSH1, UMOUNT_META, UMOUNT_FLUSH2};
    enum class AllocationState : uint8_t {IDLE, CHECK_EVENT, REQUESTING_BLOCK};
    
    template <bool Writable> class ClusterChain;
    template <bool Writable> class DirEntryRef;
    class DirectoryIterator;
    template <bool Writable> class WriteReference;
    
    APRINTER_STRUCT_IF_TEMPLATE(FsEntryExtra) {
        BlockIndexType dir_entry_block_index;
        DirEntriesPerBlockType dir_entry_block_offset;
    };
    
public:
    static size_t const TheBlockSize = BlockSize;
    
    enum class EntryType : uint8_t {DIR_TYPE, FILE_TYPE};
    
    class FsEntry : private FsEntryExtra<FsWritable> {
        friend FatFs;
        
    public:
        inline EntryType getType () const { return type; }
        inline uint32_t getFileSize () const { return file_size; }
        
    private:
        EntryType type;
        uint32_t file_size;
        ClusterIndexType cluster_index;
    };
    
    static bool isPartitionTypeSupported (uint8_t type)
    {
        return (type == 0xB || type == 0xC);
    }
    
    static void init (Context c, BlockRange<BlockIndexType> block_range)
    {
        auto *o = Object::self(c);
        
        TheBlockCache::init(c);
        
        o->block_range = block_range;
        o->state = FsState::INIT;
        
        o->init_block_ref.init(c, APRINTER_CB_STATFUNC_T(&FatFs::init_block_ref_handler));
        o->init_block_ref.requestBlock(c, get_abs_block_index(c, 0), 0, 1, true);
        
        fs_writable_init(c);
        
        TheDebugObject::init(c);
    }
    
    static void deinit (Context c)
    {
        auto *o = Object::self(c);
        TheDebugObject::deinit(c);
        
        fs_writable_deinit(c);
        
        if (o->state == FsState::INIT) {
            o->init_block_ref.deinit(c);
        }
        
        TheBlockCache::deinit(c);
    }
    
    static FsEntry getRootEntry (Context c)
    {
        auto *o = Object::self(c);
        TheDebugObject::access(c);
        AMBRO_ASSERT(o->state == FsState::READY)
        
        FsEntry entry;
        entry.type = EntryType::DIR_TYPE;
        entry.file_size = 0;
        entry.cluster_index = o->root_cluster;
        set_fs_entry_extra(&entry, 0, 0);
        return entry;
    }
    
    APRINTER_FUNCTION_IF_EXT(FsWritable, static, void, startWriteMount (Context c))
    {
        auto *o = Object::self(c);
        TheDebugObject::access(c);
        AMBRO_ASSERT(o->state == FsState::READY)
        AMBRO_ASSERT(o->write_mount_state == WriteMountState::NOT_MOUNTED)
        // implies
        AMBRO_ASSERT(o->num_write_references == 0)
        AMBRO_ASSERT(o->alloc_state == AllocationState::IDLE)
        
        o->write_mount_state = WriteMountState::MOUNT_META;
        o->write_block_ref.requestBlock(c, get_abs_block_index(c, 0), 0, 1, true);
    }
    
    APRINTER_FUNCTION_IF_EXT(FsWritable, static, void, startWriteUnmount (Context c))
    {
        auto *o = Object::self(c);
        TheDebugObject::access(c);
        AMBRO_ASSERT(o->state == FsState::READY)
        AMBRO_ASSERT(o->write_mount_state == WriteMountState::MOUNTED)
        AMBRO_ASSERT(o->num_write_references == 0)
        // implies
        AMBRO_ASSERT(o->alloc_state == AllocationState::IDLE)
        
        o->write_mount_state = WriteMountState::UMOUNT_FLUSH1;
        o->flush_request.requestFlush(c);
    }
    
    class DirLister {
    public:
        using DirListerHandler = Callback<void(Context c, bool is_error, char const *name, FsEntry entry)>;
        
        void init (Context c, FsEntry dir_entry, DirListerHandler handler)
{'fraction_non_alphanumeric': 0.09010313008865568, 'fraction_numerical': 0.013087268560400458, 'mean_word_length': 3.4949850908105176, 'pattern_counts': {'":': 0, '<': 71, '<?xml version=': 0, '>': 122, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 6, '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': '1026131', 'n_tokens_mistral': 5424, 'n_tokens_neox': 5113, 'n_words': 1297}