#pragma once

//
// Define this if you want debugging support
//
#define _CC_DEBUG_                                      0x00

//
// These define the Debug Masks Supported
//
#define CC_API_DEBUG                                    0x01

//
// Debug/Tracing support
//
#if _CC_DEBUG_
#ifdef NEW_DEBUG_SYSTEM_IMPLEMENTED // enable when Debug Filters are implemented
#define CCTRACE(x, ...)                                     \
    {                                                       \
        DbgPrintEx("%s [%.16s] - ",                         \
                   __FUNCTION__,                            \
                   PsGetCurrentProcess()->ImageFileName);   \
        DbgPrintEx(__VA_ARGS__);                            \
    }
#else
#define CCTRACE(x, ...)                                     \
    if (x & CcRosTraceLevel)                                \
    {                                                       \
        DbgPrint("%s [%.16s] - ",                           \
                 __FUNCTION__,                              \
                 PsGetCurrentProcess()->ImageFileName);     \
        DbgPrint(__VA_ARGS__);                              \
    }
#endif
#else
#define CCTRACE(x, fmt, ...) DPRINT(fmt, ##__VA_ARGS__)
#endif

//
// Global Cc Data
//
extern ULONG CcRosTraceLevel;
extern LIST_ENTRY DirtyVacbListHead;
extern ULONG CcDirtyPageThreshold;
extern ULONG CcTotalDirtyPages;
extern LIST_ENTRY CcDeferredWrites;
extern KSPIN_LOCK CcDeferredWriteSpinLock;
extern ULONG CcNumberWorkerThreads;
extern LIST_ENTRY CcIdleWorkerThreadList;
extern LIST_ENTRY CcExpressWorkQueue;
extern LIST_ENTRY CcRegularWorkQueue;
extern LIST_ENTRY CcPostTickWorkQueue;
extern NPAGED_LOOKASIDE_LIST CcTwilightLookasideList;
extern LARGE_INTEGER CcIdleDelay;

//
// Counters
//
extern ULONG CcLazyWritePages;
extern ULONG CcLazyWriteIos;
extern ULONG CcMapDataWait;
extern ULONG CcMapDataNoWait;
extern ULONG CcPinReadWait;
extern ULONG CcPinReadNoWait;
extern ULONG CcPinMappedDataCount;
extern ULONG CcDataPages;
extern ULONG CcDataFlushes;

typedef struct _PF_SCENARIO_ID
{
    WCHAR ScenName[30];
    ULONG HashId;
} PF_SCENARIO_ID, *PPF_SCENARIO_ID;

typedef struct _PF_LOG_ENTRY
{
    ULONG FileOffset:30;
    ULONG Type:2;
    union
    {
        ULONG FileKey;
        ULONG FileSequenceNumber;
    };
} PF_LOG_ENTRY, *PPF_LOG_ENTRY;

typedef struct _PFSN_LOG_ENTRIES
{
    LIST_ENTRY TraceBuffersLink;
    LONG NumEntries;
    LONG MaxEntries;
    PF_LOG_ENTRY Entries[ANYSIZE_ARRAY];
} PFSN_LOG_ENTRIES, *PPFSN_LOG_ENTRIES;

typedef struct _PF_SECTION_INFO
{
    ULONG FileKey;
    ULONG FileSequenceNumber;
    ULONG FileIdLow;
    ULONG FileIdHigh;
} PF_SECTION_INFO, *PPF_SECTION_INFO;

typedef struct _PF_TRACE_HEADER
{
    ULONG Version;
    ULONG MagicNumber;
    ULONG Size;
    PF_SCENARIO_ID ScenarioId;
    ULONG ScenarioType; // PF_SCENARIO_TYPE
    ULONG EventEntryIdxs[8];
    ULONG NumEventEntryIdxs;
    ULONG TraceBufferOffset;
    ULONG NumEntries;
    ULONG SectionInfoOffset;
    ULONG NumSections;
    ULONG FaultsPerPeriod[10];
    LARGE_INTEGER LaunchTime;
    ULONGLONG Reserved[5];
} PF_TRACE_HEADER, *PPF_TRACE_HEADER;

typedef struct _PFSN_TRACE_DUMP
{
    LIST_ENTRY CompletedTracesLink;
    PF_TRACE_HEADER Trace;
} PFSN_TRACE_DUMP, *PPFSN_TRACE_DUMP;

typedef struct _PFSN_TRACE_HEADER
{
    ULONG Magic;
    LIST_ENTRY ActiveTracesLink;
    PF_SCENARIO_ID ScenarioId;
    ULONG ScenarioType; // PF_SCENARIO_TYPE
    ULONG EventEntryIdxs[8];
    ULONG NumEventEntryIdxs;
    PPFSN_LOG_ENTRIES CurrentTraceBuffer;
    LIST_ENTRY TraceBuffersList;
    ULONG NumTraceBuffers;
    KSPIN_LOCK TraceBufferSpinLock;
    KTIMER TraceTimer;
    LARGE_INTEGER TraceTimerPeriod;
    KDPC TraceTimerDpc;
    KSPIN_LOCK TraceTimerSpinLock;
    ULONG FaultsPerPeriod[10];
    LONG LastNumFaults;
    LONG CurPeriod;
    LONG NumFaults;
    LONG MaxFaults;
    PEPROCESS Process;
    EX_RUNDOWN_REF RefCount;
    WORK_QUEUE_ITEM EndTraceWorkItem;
    LONG EndTraceCalled;
    PPFSN_TRACE_DUMP TraceDump;
    NTSTATUS TraceDumpStatus;
    LARGE_INTEGER LaunchTime;
    PPF_SECTION_INFO SectionInfo;
    ULONG SectionInfoCount;
} PFSN_TRACE_HEADER, *PPFSN_TRACE_HEADER;

typedef struct _PFSN_PREFETCHER_GLOBALS
{
    LIST_ENTRY ActiveTraces;
    KSPIN_LOCK ActiveTracesLock;
    PPFSN_TRACE_HEADER SystemWideTrace;
    LIST_ENTRY CompletedTraces;
    FAST_MUTEX CompletedTracesLock;
    LONG NumCompletedTraces;
    PKEVENT CompletedTracesEvent;
    LONG ActivePrefetches;
} PFSN_PREFETCHER_GLOBALS, *PPFSN_PREFETCHER_GLOBALS;

typedef struct _ROS_SHARED_CACHE_MAP
{
    CSHORT NodeTypeCode;
    CSHORT NodeByteSize;
    ULONG OpenCount;
    LARGE_INTEGER FileSize;
    LIST_ENTRY BcbList;
    LARGE_INTEGER SectionSize;
    LARGE_INTEGER ValidDataLength;
    PFILE_OBJECT FileObject;
    ULONG DirtyPages;
    LIST_ENTRY SharedCacheMapLinks;
    ULONG Flags;
    PVOID Section;
    PKEVENT CreateEvent;
    PCACHE_MANAGER_CALLBACKS Callbacks;
    PVOID LazyWriteContext;
    LIST_ENTRY PrivateList;
    ULONG DirtyPageThreshold;
    KSPIN_LOCK BcbSpinLock;
    PRIVATE_CACHE_MAP PrivateCacheMap;

    /* ROS specific */
    LIST_ENTRY CacheMapVacbListHead;
    BOOLEAN PinAccess;
    KSPIN_LOCK CacheMapLock;
    KGUARDED_MUTEX FlushCacheLock;
#if DBG
    BOOLEAN Trace; /* enable extra trace output for this cache map and it's VACBs */
#endif
} ROS_SHARED_CACHE_MAP, *PROS_SHARED_CACHE_MAP;

#define READAHEAD_DISABLED 0x1
#define WRITEBEHIND_DISABLED 0x2
#define SHARED_CACHE_MAP_IN_CREATION 0x4
#define SHARED_CACHE_MAP_IN_LAZYWRITE 0x8

typedef struct _ROS_VACB
{
    /* Base address of the region where the view's data is mapped. */
    PVOID BaseAddress;
    /* Are the contents of the view newer than those on disk. */
    BOOLEAN Dirty;
    /* Page out in progress */
    BOOLEAN PageOut;
    ULONG MappedCount;
    /* Entry in the list of VACBs for this shared cache map. */
    LIST_ENTRY CacheMapVacbListEntry;
    /* Entry in the list of VACBs which are dirty. */
    LIST_ENTRY DirtyVacbListEntry;
    /* Entry in the list of VACBs. */
    LIST_ENTRY VacbLruListEntry;
    /* Offset in the file which this view maps. */
    LARGE_INTEGER FileOffset;
    /* Number of references. */
    volatile ULONG ReferenceCount;
    /* Pointer to the shared cache map for the file which this view maps data for. */
    PROS_SHARED_CACHE_MAP SharedCacheMap;
    /* Pointer to the next VACB in a chain. */
} ROS_VACB, *PROS_VACB;

typedef struct _INTERNAL_BCB
{
    /* Lock */
    ERESOURCE Lock;
    PUBLIC_BCB PFCB;
    PROS_VACB Vacb;
    ULONG PinCount;
    CSHORT RefCount; /* (At offset 0x34 on WinNT4) */
    LIST_ENTRY BcbEntry;
} INTERNAL_BCB, *PINTERNAL_BCB;

typedef struct _LAZY_WRITER
{
    LIST_ENTRY WorkQueue;
    KDPC ScanDpc;
    KTIMER ScanTimer;
    BOOLEAN ScanActive;
    BOOLEAN OtherWork;
    BOOLEAN PendingTeardown;
} LAZY_WRITER, *PLAZY_WRITER;

typedef struct _WORK_QUEUE_ENTRY
{
    LIST_ENTRY WorkQueueLinks;
    union
    {
        struct
        {
            FILE_OBJECT *FileObject;
        } Read;
        struct
        {
            SHARED_CACHE_MAP *SharedCacheMap;
        } Write;
        struct
        {
            KEVENT *Event;
        } Event;
        struct
        {
            unsigned long Reason;
        } Notification;
    } Parameters;
    unsigned char Function;
} WORK_QUEUE_ENTRY, *PWORK_QUEUE_ENTRY;

typedef enum _WORK_QUEUE_FUNCTIONS
{
    ReadAhead = 1,
    WriteBehind = 2,
    LazyScan = 3,
    SetDone = 4,
} WORK_QUEUE_FUNCTIONS, *PWORK_QUEUE_FUNCTIONS;

extern LAZY_WRITER LazyWriter;

#define NODE_TYPE_DEFERRED_WRITE 0x02FC
#define NODE_TYPE_PRIVATE_MAP    0x02FE
#define NODE_TYPE_SHARED_MAP     0x02FF

CODE_SEG("INIT")
VOID
NTAPI
CcPfInitializePrefetcher(
    VOID
);

VOID
NTAPI
CcMdlReadComplete2(
    IN PFILE_OBJECT FileObject,
    IN PMDL MemoryDescriptorList
);

VOID
NTAPI
CcMdlWriteComplete2(
    IN PFILE_OBJECT FileObject,
    IN PLARGE_INTEGER FileOffset,
    IN PMDL MdlChain
);

NTSTATUS
CcRosFlushVacb(
    _In_ PROS_VACB Vacb,
    _Out_opt_ PIO_STATUS_BLOCK Iosb
);

NTSTATUS
CcRosGetVacb(
    PROS_SHARED_CACHE_MAP SharedCacheMap,
    LONGLONG FileOffset,
    PROS_VACB *Vacb
);

BOOLEAN
CcRosEnsureVacbResident(
    _In_ PROS_VACB Vacb,
    _In_ BOOLEAN Wait,
    _In_ BOOLEAN NoRead,
    _In_ ULONG Offset,
    _In_ ULONG Length
);

CODE_SEG("INIT")
VOID
NTAPI
CcInitView(VOID);

VOID
NTAPI
CcShutdownLazyWriter(VOID);

CODE_SEG("INIT")
BOOLEAN
CcInitializeCacheManager(VOID);

PROS_VACB
CcRosLookupVacb(
    PROS_SHARED_CACHE_MAP SharedCacheMap,
    LONGLONG FileOffset
);

VOID
NTAPI
CcInitCacheZeroPage(VOID);

VOID
CcRosMarkDirtyVacb(
    PROS_VACB Vacb);

BOOLEAN
CcRosUnmarkDirtyVacb(
    PROS_VACB Vacb,
    BOOLEAN LockViews);

NTSTATUS
CcRosFlushDirtyPages(
    ULONG Target,
    PULONG Count,
    BOOLEAN Wait,
    BOOLEAN CalledFromLazy
);

VOID
CcRosDereferenceCache(PFILE_OBJECT FileObject);

VOID
CcRosReferenceCache(PFILE_OBJECT FileObject);

NTSTATUS
CcRosReleaseVacb(
    PROS_SHARED_CACHE_MAP SharedCacheMap,
    PROS_VACB Vacb,
    BOOLEAN Dirty,
    BOOLEAN Mapped
);

NTSTATUS
CcRosRequestVacb(
    PROS_SHARED_CACHE_MAP SharedCacheMap,
    LONGLONG FileOffset,
    PROS_VACB *Vacb
);

NTSTATUS
CcRosInitializeFileCache(
    PFILE_OBJECT FileObject,
    PCC_FILE_SIZES FileSizes,
    BOOLEAN PinAccess,
    PCACHE_MANAGER_CALLBACKS CallBacks,
    PVOID LazyWriterContext
);

NTSTATUS
CcRosReleaseFileCache(
    PFILE_OBJECT FileObject
);

VOID
NTAPI
CcShutdownSystem(VOID);

VOID
NTAPI
CcWorkerThread(PVOID Parameter);

VOID
NTAPI
CcScanDpc(
    PKDPC Dpc,
    PVOID DeferredContext,
    PVOID SystemArgument1,
    PVOID SystemArgument2);

VOID
CcScheduleLazyWriteScan(BOOLEAN NoDelay);

VOID
CcPostDeferredWrites(VOID);

VOID
CcPostWorkQueue(
    IN PWORK_QUEUE_ENTRY WorkItem,
    IN PLIST_ENTRY WorkQueue);

VOID
CcPerformReadAhead(
    IN PFILE_OBJECT FileObject);

NTSTATUS
CcRosInternalFreeVacb(
    IN PROS_VACB Vacb);

FORCEINLINE
BOOLEAN
DoRangesIntersect(
    _In_ LONGLONG Offset1,
    _In_ LONGLONG Length1,
    _In_ LONGLONG Offset2,
    _In_ LONGLONG Length2)
{
    if (Offset1 + Length1 <= Offset2)
        return FALSE;
    if (Offset2 + Length2 <= Offset1)
        return FALSE;
    return TRUE;
}

FORCEINLINE
BOOLEAN
IsPointInRange(
    _In_ LONGLONG Offset1,
    _In_ LONGLONG Length1,
    _In_ LONGLONG Point)
{
    return DoRangesIntersect(Offset1, Length1, Point, 1);
}

#define CcBugCheck(A, B, C) KeBugCheckEx(CACHE_MANAGER, BugCheckFileId | ((ULONG)(__LINE__)), A, B, C)

#if DBG
#define CcRosVacbIncRefCount(vacb) CcRosVacbIncRefCount_(vacb,__FILE__,__LINE__)
#define CcRosVacbDecRefCount(vacb) CcRosVacbDecRefCount_(vacb,__FILE__,__LINE__)
#define CcRosVacbGetRefCount(vacb) CcRosVacbGetRefCount_(vacb,__FILE__,__LINE__)

ULONG
CcRosVacbIncRefCount_(
    PROS_VACB vacb,
    PCSTR file,
    INT line);

ULONG
CcRosVacbDecRefCount_(
    PROS_VACB vacb,
    PCSTR file,
    INT line);

ULONG
CcRosVacbGetRefCount_(
    PROS_VACB vacb,
    PCSTR file,
    INT line);

#else
#define CcRosVacbIncRefCount(vacb) InterlockedIncrement((PLONG)&(vacb)->ReferenceCount)
FORCEINLINE
ULONG
CcRosVacbDecRefCount(
    PROS_VACB vacb)
{
    ULONG Refs;

    Refs = InterlockedDecrement((PLONG)&vacb->ReferenceCount);
    if (Refs == 0)
    {
        CcRosInternalFreeVacb(vacb);
    }
    return Refs;
}
#define CcRosVacbGetRefCount(vacb) InterlockedCompareExchange((PLONG)&(vacb)->ReferenceCount, 0, 0)
#endif

BOOLEAN
CcRosFreeOneUnusedVacb(
    VOID);