xref: /reactos/sdk/lib/ucrt/heap/expand.cpp (revision 04e0dc4a)
1 //
2 // expand.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Implementation of _expand().
7 //
8 #include <corecrt_internal.h>
9 #include <malloc.h>
10 
11 
12 
13 // Enclaves have a different heap implementation.
14 
15 #ifdef _UCRT_ENCLAVE_BUILD
16 
17 // Tests whether the allocation contraction is possible
is_contraction_possible(size_t const)18 __inline static bool is_contraction_possible(size_t const) throw()
19 {
20     return FALSE;
21 }
22 
23 #else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */
24 
25 // Tests whether the allocation contraction is possible
is_contraction_possible(size_t const old_size)26 __inline static bool is_contraction_possible(size_t const old_size) throw()
27 {
28     // Check if object allocated on low fragmentation heap.
29     //The LFH can only allocate blocks up to 16KB in size.
30     if (old_size <= 0x4000)
31     {
32         LONG heap_type = -1;
33         if (!HeapQueryInformation(
34             __acrt_heap,
35             HeapCompatibilityInformation,
36             &heap_type,
37             sizeof(heap_type),
38             nullptr))
39         {
40             return FALSE;
41         }
42         return heap_type != 2;
43     }
44     // Contraction possible for objects not on the LFH
45     return TRUE;
46 }
47 
48 #endif /* _UCRT_ENCLAVE_BUILD */
49 
50 
51 // This function implements the logic of expand().  It is called directly by the
52 // _expand() function in the Release CRT, and called by the debug heap in the
53 // Debug CRT.
54 //
55 // This function must be marked noinline, otherwise _expand and
56 // _expand_base will have identical COMDATs, and the linker will fold
57 // them when calling one from the CRT. This is necessary because _expand
58 // needs to support users patching in custom implementations.
_expand_base(void * const block,size_t const size)59 extern "C" __declspec(noinline) void* __cdecl _expand_base(void* const block, size_t const size)
60 {
61     // Validation section
62     _VALIDATE_RETURN      (block != nullptr,     EINVAL, nullptr);
63     _VALIDATE_RETURN_NOEXC(size <= _HEAP_MAXREQ, ENOMEM, nullptr);
64 
65     size_t const old_size = static_cast<size_t>(HeapSize(__acrt_heap, 0, block));
66     size_t const new_size = size == 0 ? 1 : size;
67 
68     void* new_block = HeapReAlloc(__acrt_heap, HEAP_REALLOC_IN_PLACE_ONLY, block, new_size);
69     if (new_block != nullptr)
70         return new_block;
71 
72     // If a failure to contract was caused by platform limitations, just
73     // return the original block.
74     if (new_size <= old_size && !is_contraction_possible(old_size))
75         return block;
76 
77     errno = __acrt_errno_from_os_error(GetLastError());
78     return nullptr;
79 }
80 
81 // Expands or contracts a block of memory in the heap.
82 //
83 // This function resizes a block of memory in the heap to 'size' bytes.  The
84 // new size may be either greater (expansion) or less (contraction) than the
85 // original size of the block.  This function never moves the block.  In the
86 // case of expansion, if the block cannot be expanded to 'size', it is expanded
87 // as much as possible.
88 //
89 // This function supports patching and therefore must be marked noinline.
90 // Both _expand_dbg and _expand_base must also be marked noinline
91 // to prevent identical COMDAT folding from substituting calls to _expand
92 // with either other function or vice versa.
_expand(void * const block,size_t const size)93 extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) void* __cdecl _expand(void* const block, size_t const size)
94 {
95     #ifdef _DEBUG
96     return _expand_dbg(block, size, _NORMAL_BLOCK, nullptr, 0);
97     #else
98     return _expand_base(block, size);
99     #endif
100 }
101