root/src/add-ons/kernel/file_systems/btrfs/ExtentAllocator.h
/*
 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com.
 * This file may be used under the terms of the MIT License.
 */
#ifndef EXTENT_ALLOCATOR_H
#define EXTENT_ALLOCATOR_H


#include "BTree.h"
#include "Volume.h"

#include "system_dependencies.h"


#ifdef FS_SHELL
#define ERROR(x...) TRACE(x)
#else
#include <DebugSupport.h>
#endif


//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
#       define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
#       define TRACE(x...) ;
#endif


struct CachedExtent : AVLTreeNode {
        uint64          offset;
        uint64          length;
        int                     refCount;
        uint64          flags;
        btrfs_extent* item;

        static  CachedExtent*           Create(uint64 offset, uint64 length,
                                                                        uint64 flags);
                        void                            Delete();

                        uint64                          End() const { return offset + length; }
                        bool                            IsAllocated() const;
                        bool                            IsData() const;

                        void                            Info() const;

private:
                                                                CachedExtent() {}
                                                                CachedExtent(const CachedExtent& other);
};


struct TreeDefinition {
        typedef uint64                  Key;
        typedef CachedExtent    Value;

        AVLTreeNode* GetAVLTreeNode(Value* value) const
        {
                return value;
        }

        Value* GetValue(AVLTreeNode* node) const
        {
                return static_cast<Value*>(node);
        }

        int Compare(const Key& a, const Value* b) const
        {
                if (a < b->offset)
                        return -1;
                if (a >= b->End())
                        return 1;
                return 0;
        }

        int Compare(const Value* a, const Value* b) const
        {
                int comp = Compare(a->offset, b);
                // TODO: check more conditions here if necessary
                return comp;
        }
};


struct CachedExtentTree : AVLTree<TreeDefinition> {
public:
                                                                CachedExtentTree(
                                                                        const TreeDefinition& definition);
                                                                ~CachedExtentTree();

                        status_t                        FindNext(CachedExtent** chosen, uint64 offset,
                                                                        uint64 size, uint64 type);
                        status_t                        AddExtent(CachedExtent* extent);
                        status_t                        FillFreeExtents(uint64 lowerBound,
                                                                        uint64 upperBound);
                        void                            DumpInOrder() const;
                        void                            Delete();
private:
                        status_t                        _AddAllocatedExtent(CachedExtent* node);
                        status_t                        _AddFreeExtent(CachedExtent* node);
                        void                            _CombineFreeExtent(CachedExtent* node);
                        void                            _RemoveExtent(CachedExtent* node);
                        void                            _DumpInOrder(CachedExtent* node) const;
                        void                            _Delete(CachedExtent* node);
};


class BlockGroup {
public:
                                                                BlockGroup(BTree* extentTree);
                                                                ~BlockGroup();

                        status_t                        SetExtentTree(off_t rootAddress);
                        status_t                        Initialize(uint64 flag);
                        status_t                        LoadExtent(CachedExtentTree* tree,
                                                                        bool inverse = false);

                        uint64                          Flags() const { return fItem->Flags(); }
                        uint64                          Start() const { return fKey.ObjectID(); }
                        uint64                          End() const
                                                                        { return fKey.ObjectID() + fKey.Offset(); }

private:
                                                                BlockGroup(const BlockGroup&);
                                                                BlockGroup& operator=(const BlockGroup&);
                        status_t                        _InsertExtent(CachedExtentTree* tree,
                                                                        uint64 size, uint64 length, uint64 flags);
                        status_t                        _InsertExtent(CachedExtentTree* tree,
                                                                        CachedExtent* extent);

private:
                        btrfs_key                       fKey;
                        btrfs_block_group*      fItem;
                        BTree*                          fCurrentExtentTree;
};


class ExtentAllocator {
public:
                                                                ExtentAllocator(Volume* volume);
                                                                ~ExtentAllocator();

                        status_t                        Initialize();
                        status_t                        AllocateTreeBlock(uint64& found,
                                                                        uint64 start = (uint64)-1,
                                                                        uint64 flags = BTRFS_BLOCKGROUP_FLAG_METADATA);
                        status_t                        AllocateDataBlock(uint64& found, uint64 size,
                                                                        uint64 start = (uint64)-1,
                                                                        uint64 flags = BTRFS_BLOCKGROUP_FLAG_DATA);
private:
                                                                ExtentAllocator(const ExtentAllocator&);
                                                                ExtentAllocator& operator=(const ExtentAllocator&);
                        status_t                        _LoadExtentTree(uint64 flags);
                        status_t                        _Allocate(uint64& found, uint64 start,
                                                                        uint64 size, uint64 type);
private:
                        Volume*                         fVolume;
                        CachedExtentTree*       fTree;
                        uint64                          fStart;
                        uint64                          fEnd;
};


#endif  // EXTENT_ALLOCATOR_H