From de743e0f9b8e34d5c203116f7008410f6ed0e011 Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Sat, 9 Oct 2010 13:02:34 +0000 Subject: [PATCH] [HEAP] - Move (and cleanup) private heap definitions and structure to a standalone header file heap.h. It's explicitly included in heap_rewrite.c and heapdbg.c to prevent conflicts with the existing heap manager in heap.c. svn path=/trunk/; revision=49071 --- reactos/lib/rtl/heap.h | 297 +++++++++++++++++++++++++++++++++ reactos/lib/rtl/heap_rewrite.c | 278 +----------------------------- reactos/lib/rtl/heapdbg.c | 12 ++ 3 files changed, 311 insertions(+), 276 deletions(-) create mode 100644 reactos/lib/rtl/heap.h diff --git a/reactos/lib/rtl/heap.h b/reactos/lib/rtl/heap.h new file mode 100644 index 00000000000..b7e493b723c --- /dev/null +++ b/reactos/lib/rtl/heap.h @@ -0,0 +1,297 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS System Libraries + * FILE: lib/rtl/heap.h + * PURPOSE: Run-Time Libary Heap Manager header + * PROGRAMMER: Aleksey Bragin + */ + +/* INCLUDES ******************************************************************/ + +#ifndef RTL_HEAP_H +#define RTL_HEAP_H + +/* Core heap definitions */ +#define HEAP_FREELISTS 128 +#define HEAP_SEGMENTS 64 + +#define HEAP_ENTRY_SIZE ((ULONG)sizeof(HEAP_ENTRY)) +#define HEAP_ENTRY_SHIFT 3 +#define HEAP_MAX_BLOCK_SIZE ((0x80000 - PAGE_SIZE) >> HEAP_ENTRY_SHIFT) + +#define ARENA_INUSE_FILLER 0xBAADF00D +#define ARENA_FREE_FILLER 0xFEEEFEEE +#define HEAP_TAIL_FILL 0xab + +// from ntifs.h, should go to another header! +#define HEAP_GLOBAL_TAG 0x0800 +#define HEAP_PSEUDO_TAG_FLAG 0x8000 +#define HEAP_TAG_MASK (HEAP_MAXIMUM_TAG << HEAP_TAG_SHIFT) + +#define HEAP_EXTRA_FLAGS_MASK (HEAP_CAPTURE_STACK_BACKTRACES | \ + HEAP_SETTABLE_USER_VALUE | \ + (HEAP_TAG_MASK ^ (0xFF << HEAP_TAG_SHIFT))) + +/* Heap entry flags */ +#define HEAP_ENTRY_BUSY 0x01 +#define HEAP_ENTRY_EXTRA_PRESENT 0x02 +#define HEAP_ENTRY_FILL_PATTERN 0x04 +#define HEAP_ENTRY_VIRTUAL_ALLOC 0x08 +#define HEAP_ENTRY_LAST_ENTRY 0x10 +#define HEAP_ENTRY_SETTABLE_FLAG1 0x20 +#define HEAP_ENTRY_SETTABLE_FLAG2 0x40 +#define HEAP_ENTRY_SETTABLE_FLAG3 0x80 +#define HEAP_ENTRY_SETTABLE_FLAGS (HEAP_ENTRY_SETTABLE_FLAG1 | HEAP_ENTRY_SETTABLE_FLAG2 | HEAP_ENTRY_SETTABLE_FLAG3) + +/* Signatures */ +#define HEAP_SIGNATURE 0xeefeeff +#define HEAP_SEGMENT_SIGNATURE 0xffeeffee + +/* Segment flags */ +#define HEAP_USER_ALLOCATED 0x1 + +/* Heap structures */ +struct _HEAP_COMMON_ENTRY +{ + union + { + struct + { + USHORT Size; + UCHAR Flags; + UCHAR SmallTagIndex; + }; + struct + { + PVOID SubSegmentCode; + USHORT PreviousSize; + union + { + UCHAR SegmentOffset; + UCHAR LFHFlags; + }; + UCHAR UnusedBytes; + }; + struct + { + USHORT FunctionIndex; + USHORT ContextValue; + }; + struct + { + ULONG InterceptorValue; + USHORT UnusedBytesLength; + UCHAR EntryOffset; + UCHAR ExtendedBlockSignature; + }; + struct + { + ULONG Code1; + USHORT Code2; + UCHAR Code3; + UCHAR Code4; + }; + ULONGLONG AgregateCode; + }; +}; + +typedef struct _HEAP_FREE_ENTRY +{ + struct _HEAP_COMMON_ENTRY; + LIST_ENTRY FreeList; +} HEAP_FREE_ENTRY, *PHEAP_FREE_ENTRY; + +typedef struct _HEAP_ENTRY +{ + struct _HEAP_COMMON_ENTRY; +} HEAP_ENTRY, *PHEAP_ENTRY; + +C_ASSERT(sizeof(HEAP_ENTRY) == 8); +C_ASSERT((1 << HEAP_ENTRY_SHIFT) == sizeof(HEAP_ENTRY)); + +typedef struct _HEAP_TAG_ENTRY +{ + ULONG Allocs; + ULONG Frees; + ULONG Size; + USHORT TagIndex; + USHORT CreatorBackTraceIndex; + WCHAR TagName[24]; +} HEAP_TAG_ENTRY, *PHEAP_TAG_ENTRY; + +typedef struct _HEAP_PSEUDO_TAG_ENTRY +{ + ULONG Allocs; + ULONG Frees; + ULONG Size; +} HEAP_PSEUDO_TAG_ENTRY, *PHEAP_PSEUDO_TAG_ENTRY; + +typedef struct _HEAP_COUNTERS +{ + ULONG TotalMemoryReserved; + ULONG TotalMemoryCommitted; + ULONG TotalMemoryLargeUCR; + ULONG TotalSizeInVirtualBlocks; + ULONG TotalSegments; + ULONG TotalUCRs; + ULONG CommittOps; + ULONG DeCommitOps; + ULONG LockAcquires; + ULONG LockCollisions; + ULONG CommitRate; + ULONG DecommittRate; + ULONG CommitFailures; + ULONG InBlockCommitFailures; + ULONG CompactHeapCalls; + ULONG CompactedUCRs; + ULONG InBlockDeccommits; + ULONG InBlockDeccomitSize; +} HEAP_COUNTERS, *PHEAP_COUNTERS; + +typedef struct _HEAP_TUNING_PARAMETERS +{ + ULONG CommittThresholdShift; + ULONG MaxPreCommittThreshold; +} HEAP_TUNING_PARAMETERS, *PHEAP_TUNING_PARAMETERS; + +typedef struct _HEAP +{ + HEAP_ENTRY Entry; + ULONG SegmentSignature; + ULONG SegmentFlags; + LIST_ENTRY SegmentListEntry; + struct _HEAP *Heap; + PVOID BaseAddress; + ULONG NumberOfPages; + PHEAP_ENTRY FirstEntry; + PHEAP_ENTRY LastValidEntry; + ULONG NumberOfUnCommittedPages; + ULONG NumberOfUnCommittedRanges; + USHORT SegmentAllocatorBackTraceIndex; + USHORT Reserved; + LIST_ENTRY UCRSegmentList; + ULONG Flags; + ULONG ForceFlags; + ULONG CompatibilityFlags; + ULONG EncodeFlagMask; + HEAP_ENTRY Encoding; + ULONG PointerKey; + ULONG Interceptor; + ULONG VirtualMemoryThreshold; + ULONG Signature; + ULONG SegmentReserve; + ULONG SegmentCommit; + ULONG DeCommitFreeBlockThreshold; + ULONG DeCommitTotalFreeThreshold; + ULONG TotalFreeSize; + ULONG MaximumAllocationSize; + USHORT ProcessHeapsListIndex; + USHORT HeaderValidateLength; + PVOID HeaderValidateCopy; + USHORT NextAvailableTagIndex; + USHORT MaximumTagIndex; + PHEAP_TAG_ENTRY TagEntries; + LIST_ENTRY UCRList; + ULONG AlignRound; + ULONG AlignMask; + LIST_ENTRY VirtualAllocdBlocks; + LIST_ENTRY SegmentList; + struct _HEAP_SEGMENT *Segments[HEAP_SEGMENTS]; //FIXME: non-Vista + USHORT AllocatorBackTraceIndex; + ULONG NonDedicatedListLength; + PVOID BlocksIndex; + PVOID UCRIndex; + PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries; + LIST_ENTRY FreeLists[HEAP_FREELISTS]; //FIXME: non-Vista + union + { + ULONG FreeListsInUseUlong[HEAP_FREELISTS / (sizeof(ULONG) * 8)]; //FIXME: non-Vista + UCHAR FreeListsInUseBytes[HEAP_FREELISTS / (sizeof(UCHAR) * 8)]; //FIXME: non-Vista + } u; + PHEAP_LOCK LockVariable; + PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; + PVOID FrontEndHeap; + USHORT FrontHeapLockCount; + UCHAR FrontEndHeapType; + HEAP_COUNTERS Counters; + HEAP_TUNING_PARAMETERS TuningParameters; +} HEAP, *PHEAP; + +typedef struct _HEAP_SEGMENT +{ + HEAP_ENTRY Entry; + ULONG SegmentSignature; + ULONG SegmentFlags; + LIST_ENTRY SegmentListEntry; + PHEAP Heap; + PVOID BaseAddress; + ULONG NumberOfPages; + PHEAP_ENTRY FirstEntry; + PHEAP_ENTRY LastValidEntry; + ULONG NumberOfUnCommittedPages; + ULONG NumberOfUnCommittedRanges; + USHORT SegmentAllocatorBackTraceIndex; + USHORT Reserved; + LIST_ENTRY UCRSegmentList; + PHEAP_ENTRY LastEntryInSegment; //FIXME: non-Vista +} HEAP_SEGMENT, *PHEAP_SEGMENT; + +typedef struct _HEAP_UCR_DESCRIPTOR +{ + LIST_ENTRY ListEntry; + LIST_ENTRY SegmentEntry; + PVOID Address; + ULONG Size; +} HEAP_UCR_DESCRIPTOR, *PHEAP_UCR_DESCRIPTOR; + +typedef struct _HEAP_ENTRY_EXTRA +{ + union + { + struct + { + USHORT AllocatorBackTraceIndex; + USHORT TagIndex; + ULONG_PTR Settable; + }; + UINT64 ZeroInit; + }; +} HEAP_ENTRY_EXTRA, *PHEAP_ENTRY_EXTRA; + +typedef HEAP_ENTRY_EXTRA HEAP_FREE_ENTRY_EXTRA, *PHEAP_FREE_ENTRY_EXTRA; + +typedef struct _HEAP_VIRTUAL_ALLOC_ENTRY +{ + LIST_ENTRY Entry; + HEAP_ENTRY_EXTRA ExtraStuff; + ULONG CommitSize; + ULONG ReserveSize; + HEAP_ENTRY BusyBlock; +} HEAP_VIRTUAL_ALLOC_ENTRY, *PHEAP_VIRTUAL_ALLOC_ENTRY; + +/* Global variables */ +extern HEAP_LOCK RtlpProcessHeapsListLock; +extern BOOLEAN RtlpPageHeapEnabled; + +/* Functions declarations */ + +/* heap.c */ +PHEAP_FREE_ENTRY NTAPI +RtlpCoalesceFreeBlocks (PHEAP Heap, + PHEAP_FREE_ENTRY FreeEntry, + PSIZE_T FreeSize, + BOOLEAN Remove); + +PHEAP_ENTRY_EXTRA NTAPI +RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry); + +/* heapdbg.c */ +HANDLE NTAPI +RtlpPageHeapCreate(ULONG Flags, + PVOID Addr, + SIZE_T TotalSize, + SIZE_T CommitSize, + PVOID Lock, + PRTL_HEAP_PARAMETERS Parameters); + +#endif diff --git a/reactos/lib/rtl/heap_rewrite.c b/reactos/lib/rtl/heap_rewrite.c index 16e4873a03f..5165b5dd3bf 100644 --- a/reactos/lib/rtl/heap_rewrite.c +++ b/reactos/lib/rtl/heap_rewrite.c @@ -18,278 +18,13 @@ /* INCLUDES *****************************************************************/ #include +#include #define NDEBUG #include -// Various defines, to be moved to a separate header file -#define HEAP_FREELISTS 128 -#define HEAP_SEGMENTS 64 - -#define HEAP_ENTRY_SIZE ((ULONG)sizeof(HEAP_ENTRY)) -#define HEAP_ENTRY_SHIFT 3 -#define HEAP_MAX_BLOCK_SIZE ((0x80000 - PAGE_SIZE) >> HEAP_ENTRY_SHIFT) - -#define ARENA_INUSE_FILLER 0xBAADF00D -#define ARENA_FREE_FILLER 0xFEEEFEEE -#define HEAP_TAIL_FILL 0xab - -// from ntifs.h, should go to another header! -#define HEAP_GLOBAL_TAG 0x0800 -#define HEAP_PSEUDO_TAG_FLAG 0x8000 -#define HEAP_TAG_MASK (HEAP_MAXIMUM_TAG << HEAP_TAG_SHIFT) - -#define HEAP_EXTRA_FLAGS_MASK (HEAP_CAPTURE_STACK_BACKTRACES | \ - HEAP_SETTABLE_USER_VALUE | \ - (HEAP_TAG_MASK ^ (0xFF << HEAP_TAG_SHIFT))) - -struct _HEAP_COMMON_ENTRY -{ - union - { - struct - { - USHORT Size; // 0x0 - UCHAR Flags; // 0x2 - UCHAR SmallTagIndex; //0x3 - }; - struct - { - PVOID SubSegmentCode; // 0x0 - USHORT PreviousSize; // 0x4 - union - { - UCHAR SegmentOffset; // 0x6 - UCHAR LFHFlags; // 0x6 - }; - UCHAR UnusedBytes; // 0x7 - }; - struct - { - USHORT FunctionIndex; // 0x0 - USHORT ContextValue; // 0x2 - }; - struct - { - ULONG InterceptorValue; // 0x0 - USHORT UnusedBytesLength; // 0x4 - UCHAR EntryOffset; // 0x6 - UCHAR ExtendedBlockSignature; // 0x7 - }; - struct - { - ULONG Code1; // 0x0 - USHORT Code2; // 0x4 - UCHAR Code3; // 0x6 - UCHAR Code4; // 0x7 - }; - ULONGLONG AgregateCode; // 0x0 - }; -}; - -typedef struct _HEAP_FREE_ENTRY -{ - struct _HEAP_COMMON_ENTRY; - LIST_ENTRY FreeList; // 0x8 -} HEAP_FREE_ENTRY, *PHEAP_FREE_ENTRY; - -typedef struct _HEAP_ENTRY -{ - struct _HEAP_COMMON_ENTRY; -} HEAP_ENTRY, *PHEAP_ENTRY; - -C_ASSERT(sizeof(HEAP_ENTRY) == 8); -C_ASSERT((1 << HEAP_ENTRY_SHIFT) == sizeof(HEAP_ENTRY)); - -typedef struct _HEAP_TAG_ENTRY -{ - ULONG Allocs; - ULONG Frees; - ULONG Size; - USHORT TagIndex; - USHORT CreatorBackTraceIndex; - WCHAR TagName[24]; -} HEAP_TAG_ENTRY, *PHEAP_TAG_ENTRY; - -typedef struct _HEAP_PSEUDO_TAG_ENTRY -{ - ULONG Allocs; - ULONG Frees; - ULONG Size; -} HEAP_PSEUDO_TAG_ENTRY, *PHEAP_PSEUDO_TAG_ENTRY; - -typedef struct _HEAP_COUNTERS -{ - ULONG TotalMemoryReserved; - ULONG TotalMemoryCommitted; - ULONG TotalMemoryLargeUCR; - ULONG TotalSizeInVirtualBlocks; - ULONG TotalSegments; - ULONG TotalUCRs; - ULONG CommittOps; - ULONG DeCommitOps; - ULONG LockAcquires; - ULONG LockCollisions; - ULONG CommitRate; - ULONG DecommittRate; - ULONG CommitFailures; - ULONG InBlockCommitFailures; - ULONG CompactHeapCalls; - ULONG CompactedUCRs; - ULONG InBlockDeccommits; - ULONG InBlockDeccomitSize; -} HEAP_COUNTERS, *PHEAP_COUNTERS; - -typedef struct _HEAP_TUNING_PARAMETERS -{ - ULONG CommittThresholdShift; - ULONG MaxPreCommittThreshold; -} HEAP_TUNING_PARAMETERS, *PHEAP_TUNING_PARAMETERS; - -typedef struct _HEAP -{ - HEAP_ENTRY Entry; - ULONG SegmentSignature; - ULONG SegmentFlags; - LIST_ENTRY SegmentListEntry; - struct _HEAP *Heap; - PVOID BaseAddress; - ULONG NumberOfPages; - PHEAP_ENTRY FirstEntry; - PHEAP_ENTRY LastValidEntry; - ULONG NumberOfUnCommittedPages; - ULONG NumberOfUnCommittedRanges; - USHORT SegmentAllocatorBackTraceIndex; - USHORT Reserved; - LIST_ENTRY UCRSegmentList; - ULONG Flags; - ULONG ForceFlags; - ULONG CompatibilityFlags; - ULONG EncodeFlagMask; - HEAP_ENTRY Encoding; - ULONG PointerKey; - ULONG Interceptor; - ULONG VirtualMemoryThreshold; - ULONG Signature; - ULONG SegmentReserve; - ULONG SegmentCommit; - ULONG DeCommitFreeBlockThreshold; - ULONG DeCommitTotalFreeThreshold; - ULONG TotalFreeSize; - ULONG MaximumAllocationSize; - USHORT ProcessHeapsListIndex; - USHORT HeaderValidateLength; - PVOID HeaderValidateCopy; - USHORT NextAvailableTagIndex; - USHORT MaximumTagIndex; - PHEAP_TAG_ENTRY TagEntries; - LIST_ENTRY UCRList; - ULONG AlignRound; - ULONG AlignMask; - LIST_ENTRY VirtualAllocdBlocks; - LIST_ENTRY SegmentList; - struct _HEAP_SEGMENT *Segments[HEAP_SEGMENTS]; //FIXME: non-Vista - USHORT AllocatorBackTraceIndex; - ULONG NonDedicatedListLength; - PVOID BlocksIndex; - PVOID UCRIndex; - PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries; - LIST_ENTRY FreeLists[HEAP_FREELISTS]; //FIXME: non-Vista - union - { - ULONG FreeListsInUseUlong[HEAP_FREELISTS / (sizeof(ULONG) * 8)]; //FIXME: non-Vista - UCHAR FreeListsInUseBytes[HEAP_FREELISTS / (sizeof(UCHAR) * 8)]; //FIXME: non-Vista - } u; - PHEAP_LOCK LockVariable; - PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; - PVOID FrontEndHeap; - USHORT FrontHeapLockCount; - UCHAR FrontEndHeapType; - HEAP_COUNTERS Counters; - HEAP_TUNING_PARAMETERS TuningParameters; -} HEAP, *PHEAP; - -typedef struct _HEAP_SEGMENT -{ - HEAP_ENTRY Entry; - ULONG SegmentSignature; - ULONG SegmentFlags; - LIST_ENTRY SegmentListEntry; - PHEAP Heap; - PVOID BaseAddress; - ULONG NumberOfPages; - PHEAP_ENTRY FirstEntry; - PHEAP_ENTRY LastValidEntry; - ULONG NumberOfUnCommittedPages; - ULONG NumberOfUnCommittedRanges; - USHORT SegmentAllocatorBackTraceIndex; - USHORT Reserved; - LIST_ENTRY UCRSegmentList; - PHEAP_ENTRY LastEntryInSegment; //FIXME: non-Vista -} HEAP_SEGMENT, *PHEAP_SEGMENT; - -typedef struct _HEAP_UCR_DESCRIPTOR -{ - LIST_ENTRY ListEntry; - LIST_ENTRY SegmentEntry; - PVOID Address; - ULONG Size; -} HEAP_UCR_DESCRIPTOR, *PHEAP_UCR_DESCRIPTOR; - -typedef struct _HEAP_ENTRY_EXTRA -{ - union - { - struct - { - USHORT AllocatorBackTraceIndex; - USHORT TagIndex; - ULONG_PTR Settable; - }; - UINT64 ZeroInit; - }; -} HEAP_ENTRY_EXTRA, *PHEAP_ENTRY_EXTRA; - -typedef HEAP_ENTRY_EXTRA HEAP_FREE_ENTRY_EXTRA, *PHEAP_FREE_ENTRY_EXTRA; - -typedef struct _HEAP_VIRTUAL_ALLOC_ENTRY -{ - LIST_ENTRY Entry; - HEAP_ENTRY_EXTRA ExtraStuff; - ULONG CommitSize; - ULONG ReserveSize; - HEAP_ENTRY BusyBlock; -} HEAP_VIRTUAL_ALLOC_ENTRY, *PHEAP_VIRTUAL_ALLOC_ENTRY; - -extern BOOLEAN RtlpPageHeapEnabled; -HANDLE NTAPI -RtlpSpecialHeapCreate(ULONG Flags, - PVOID Addr, - SIZE_T TotalSize, - SIZE_T CommitSize, - PVOID Lock, - PRTL_HEAP_PARAMETERS Parameters) { return NULL; }; - HEAP_LOCK RtlpProcessHeapsListLock; -/* Heap entry flags */ -#define HEAP_ENTRY_BUSY 0x01 -#define HEAP_ENTRY_EXTRA_PRESENT 0x02 -#define HEAP_ENTRY_FILL_PATTERN 0x04 -#define HEAP_ENTRY_VIRTUAL_ALLOC 0x08 -#define HEAP_ENTRY_LAST_ENTRY 0x10 -#define HEAP_ENTRY_SETTABLE_FLAG1 0x20 -#define HEAP_ENTRY_SETTABLE_FLAG2 0x40 -#define HEAP_ENTRY_SETTABLE_FLAG3 0x80 -#define HEAP_ENTRY_SETTABLE_FLAGS (HEAP_ENTRY_SETTABLE_FLAG1 | HEAP_ENTRY_SETTABLE_FLAG2 | HEAP_ENTRY_SETTABLE_FLAG3) - -/* Signatures */ -#define HEAP_SIGNATURE 0xeefeeff -#define HEAP_SEGMENT_SIGNATURE 0xffeeffee - -/* Segment flags */ -#define HEAP_USER_ALLOCATED 0x1 - /* Bitmaps stuff */ /* How many least significant bits are clear */ @@ -335,15 +70,6 @@ RtlpFindLeastSetBit(ULONG Bits) ULONG NTAPI RtlCompareMemoryUlong(PVOID Source, ULONG Length, ULONG Value); -PHEAP_FREE_ENTRY NTAPI -RtlpCoalesceFreeBlocks (PHEAP Heap, - PHEAP_FREE_ENTRY FreeEntry, - PSIZE_T FreeSize, - BOOLEAN Remove); - -PHEAP_ENTRY_EXTRA NTAPI -RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry); - /* FUNCTIONS *****************************************************************/ VOID NTAPI @@ -1546,7 +1272,7 @@ RtlCreateHeap(ULONG Flags, /* Check for a special heap */ if (RtlpPageHeapEnabled && !Addr && !Lock) { - Heap = RtlpSpecialHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); + Heap = RtlpPageHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); if (Heap) return Heap; //ASSERT(FALSE); diff --git a/reactos/lib/rtl/heapdbg.c b/reactos/lib/rtl/heapdbg.c index d7bda960398..242dfacaf9b 100644 --- a/reactos/lib/rtl/heapdbg.c +++ b/reactos/lib/rtl/heapdbg.c @@ -9,6 +9,7 @@ /* INCLUDES ******************************************************************/ #include +#include #define NDEBUG #include @@ -21,4 +22,15 @@ WCHAR RtlpPageHeapTargetDlls[512]; /* FUNCTIONS ******************************************************************/ +HANDLE NTAPI +RtlpPageHeapCreate(ULONG Flags, + PVOID Addr, + SIZE_T TotalSize, + SIZE_T CommitSize, + PVOID Lock, + PRTL_HEAP_PARAMETERS Parameters) +{ + return NULL; +} + /* EOF */ \ No newline at end of file