#ifndef _PH_FILEPOOL_H
#define _PH_FILEPOOL_H

#ifdef __cplusplus
extern "C" {
#endif

// On-disk structures

// Each file has at least one segment. Each segment has a number of blocks, which are allocated from
// a bitmap. The segment header is always in the first block of each segment, except for the first
// segment. In the first segment, the file header is in the first few blocks, followed by the
// segment header.
//
// The segments are placed in a particular free list depending on how many blocks they have free;
// this allows allocators to simply skip the segments which don't have enough segments free, and
// allocate new segments if necessary. The free list does not however guarantee that a particular
// segment has a particular number of contiguous blocks free; low performance can still occur when
// there is fragmentation.

/** The number of 32-bit integers used for each allocation bitmap. */
#define PH_FP_BITMAP_SIZE 64
/** The power-of-two index of the bitmap size. */
#define PH_FP_BITMAP_SIZE_SHIFT 6
/** The number of blocks that are available in each segment. */
#define PH_FP_BLOCK_COUNT (PH_FP_BITMAP_SIZE * 32)
/** The power-of-two index of the block count. */
#define PH_FP_BLOCK_COUNT_SHIFT (PH_FP_BITMAP_SIZE_SHIFT + 5)
/** The number of free lists for segments. */
#define PH_FP_FREE_LIST_COUNT 8

// Block flags
/** The block is the beginning of a large allocation (one that spans several segments). */
#define PH_FP_BLOCK_LARGE_ALLOCATION 0x1

typedef struct _PH_FP_BLOCK_HEADER
{
    ULONG Flags; // PH_FP_BLOCK_*
    /** The number of blocks in the entire logical block, or the number
     * of segments in a large allocation. */
    ULONG Span;
    ULONGLONG Body;
} PH_FP_BLOCK_HEADER, *PPH_FP_BLOCK_HEADER;

typedef struct _PH_FP_SEGMENT_HEADER
{
    ULONG Bitmap[PH_FP_BITMAP_SIZE];
    ULONG FreeBlocks;
    ULONG FreeFlink;
    ULONG FreeBlink;
    ULONG Reserved[13];
} PH_FP_SEGMENT_HEADER, *PPH_FP_SEGMENT_HEADER;

#define PH_FP_MAGIC ('loPF')

typedef struct _PH_FP_FILE_HEADER
{
    ULONG Magic;
    ULONG SegmentShift;
    ULONG SegmentCount;
    ULONGLONG UserContext;
    ULONG FreeLists[PH_FP_FREE_LIST_COUNT];
} PH_FP_FILE_HEADER, *PPH_FP_FILE_HEADER;

// Runtime

typedef struct _PH_FILE_POOL_PARAMETERS
{
    // File options

    /**
     * The base-2 logarithm of the size of each segment. This value must be between 16 and 28,
     * inclusive.
     */
    ULONG SegmentShift;

    // Runtime options

    /** The maximum number of inactive segments to keep mapped. */
    ULONG MaximumInactiveViews;
} PH_FILE_POOL_PARAMETERS, *PPH_FILE_POOL_PARAMETERS;

typedef struct _PH_FILE_POOL
{
    HANDLE FileHandle;
    HANDLE SectionHandle;
    BOOLEAN ReadOnly;

    PH_FREE_LIST ViewFreeList;
    PLIST_ENTRY *ByIndexBuckets;
    ULONG ByIndexSize;
    PH_AVL_TREE ByBaseSet;

    ULONG MaximumInactiveViews;
    ULONG NumberOfInactiveViews;
    LIST_ENTRY InactiveViewsListHead;

    PPH_FP_BLOCK_HEADER FirstBlockOfFirstSegment;
    PPH_FP_FILE_HEADER Header;
    ULONG SegmentShift; // The power-of-two size of each segment
    ULONG SegmentSize; // The size of each segment
    ULONG BlockShift; // The power-of-two size of each block in each segment
    ULONG BlockSize; // The size of each block in each segment
    ULONG FileHeaderBlockSpan; // The number of blocks needed to store a file header
    ULONG SegmentHeaderBlockSpan; // The number of blocks needed to store a segment header
} PH_FILE_POOL, *PPH_FILE_POOL;

PHLIBAPI
NTSTATUS PhCreateFilePool(
    _Out_ PPH_FILE_POOL *Pool,
    _In_ HANDLE FileHandle,
    _In_ BOOLEAN ReadOnly,
    _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters
    );

PHLIBAPI
NTSTATUS PhCreateFilePool2(
    _Out_ PPH_FILE_POOL *Pool,
    _In_ PWSTR FileName,
    _In_ BOOLEAN ReadOnly,
    _In_ ULONG ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_opt_ PPH_FILE_POOL_PARAMETERS Parameters
    );

PHLIBAPI
VOID PhDestroyFilePool(
    _In_ _Post_invalid_ PPH_FILE_POOL Pool
    );

PHLIBAPI
PVOID PhAllocateFilePool(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ ULONG Size,
    _Out_opt_ PULONG Rva
    );

PHLIBAPI
VOID PhFreeFilePool(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ PVOID Block
    );

PHLIBAPI
BOOLEAN PhFreeFilePoolByRva(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ ULONG Rva
    );

PHLIBAPI
VOID PhReferenceFilePool(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ PVOID Address
    );

PHLIBAPI
VOID PhDereferenceFilePool(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ PVOID Address
    );

PHLIBAPI
PVOID PhReferenceFilePoolByRva(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ ULONG Rva
    );

PHLIBAPI
BOOLEAN PhDereferenceFilePoolByRva(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ ULONG Rva
    );

PHLIBAPI
ULONG PhEncodeRvaFilePool(
    _In_ PPH_FILE_POOL Pool,
    _In_ PVOID Address
    );

PHLIBAPI
VOID PhGetUserContextFilePool(
    _In_ PPH_FILE_POOL Pool,
    _Out_ PULONGLONG Context
    );

PHLIBAPI
VOID PhSetUserContextFilePool(
    _Inout_ PPH_FILE_POOL Pool,
    _In_ PULONGLONG Context
    );

#ifdef __cplusplus
}
#endif

#endif
