xref: /reactos/win32ss/printing/base/spoolss/memory.c (revision 682f85ad)
1 /*
2  * PROJECT:     ReactOS Spooler Router
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Functions for allocating and freeing memory
5  * COPYRIGHT:   Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 
11 /**
12  * @name AlignRpcPtr
13  *
14  * Checks if the input buffer and buffer size are 4-byte aligned.
15  * If the buffer size is not 4-byte aligned, it is aligned down.
16  * If the input buffer is not 4-byte aligned, a 4-byte aligned buffer of the aligned down buffer size is allocated and returned.
17  *
18  * @param pBuffer
19  * The buffer to check.
20  *
21  * @param pcbBuffer
22  * Pointer to the buffer size to check. Its value is aligned down if needed.
23  *
24  * @return
25  * pBuffer if pBuffer is already 4-byte aligned, or a newly allocated 4-byte aligned buffer of the aligned down buffer size otherwise.
26  * If a buffer was allocated, you have to free it using UndoAlignRpcPtr.
27  */
28 PVOID WINAPI
29 AlignRpcPtr(PVOID pBuffer, PDWORD pcbBuffer)
30 {
31     ASSERT(pcbBuffer);
32 
33     // Align down the buffer size in pcbBuffer to a 4-byte boundary.
34     *pcbBuffer -= *pcbBuffer % sizeof(DWORD);
35 
36     // Check if pBuffer is 4-byte aligned. If not, allocate a 4-byte aligned buffer.
37     if ((ULONG_PTR)pBuffer % sizeof(DWORD))
38         pBuffer = DllAllocSplMem(*pcbBuffer);
39 
40     return pBuffer;
41 }
42 
43 /**
44  * @name AllocSplStr
45  *
46  * Allocates memory for a Unicode string and copies the input string into it.
47  * Equivalent of wcsdup, but the returned buffer is allocated from the spooler heap and must be freed with DllFreeSplStr.
48  *
49  * @param pwszInput
50  * The input string to copy
51  *
52  * @return
53  * Pointer to the copied string or NULL if no memory could be allocated.
54  */
55 PWSTR WINAPI
56 AllocSplStr(PCWSTR pwszInput)
57 {
58     DWORD cbInput;
59     PWSTR pwszOutput;
60 
61     // Sanity check
62     if (!pwszInput)
63         return NULL;
64 
65     // Get the length of the input string.
66     cbInput = (wcslen(pwszInput) + 1) * sizeof(WCHAR);
67 
68     // Allocate it. We don't use DllAllocSplMem here, because it unnecessarily zeroes the memory.
69     pwszOutput = HeapAlloc(hProcessHeap, 0, cbInput);
70     if (!pwszOutput)
71     {
72         ERR("HeapAlloc failed!\n");
73         return NULL;
74     }
75 
76     // Copy the string and return it.
77     CopyMemory(pwszOutput, pwszInput, cbInput);
78     return pwszOutput;
79 }
80 
81 /**
82  * @name DllAllocSplMem
83  *
84  * Allocate a block of zeroed memory.
85  * Windows allocates from a separate spooler heap here while we just use the process heap.
86  *
87  * @param dwBytes
88  * Number of bytes to allocate.
89  *
90  * @return
91  * A pointer to the allocated memory or NULL in case of an error.
92  * You have to free this memory using DllFreeSplMem.
93  */
94 PVOID WINAPI
95 DllAllocSplMem(DWORD dwBytes)
96 {
97     return HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwBytes);
98 }
99 
100 /**
101  * @name DllFreeSplMem
102  *
103  * Frees the memory allocated with DllAllocSplMem.
104  *
105  * @param pMem
106  * Pointer to the allocated memory.
107  *
108  * @return
109  * TRUE in case of success, FALSE otherwise.
110  */
111 BOOL WINAPI
112 DllFreeSplMem(PVOID pMem)
113 {
114     if ( !pMem ) return TRUE;
115     return HeapFree(hProcessHeap, 0, pMem);
116 }
117 
118 /**
119  * @name DllFreeSplStr
120  *
121  * Frees the string allocated with AllocSplStr.
122  *
123  * @param pwszString
124  * Pointer to the allocated string.
125  *
126  * @return
127  * TRUE in case of success, FALSE otherwise.
128  */
129 BOOL WINAPI
130 DllFreeSplStr(PWSTR pwszString)
131 {
132     if ( pwszString )
133        return HeapFree(hProcessHeap, 0, pwszString);
134     return FALSE;
135 }
136 
137 /**
138  * @name ReallocSplMem
139  *
140  * Allocates a new block of memory and copies the contents of the old block into the new one.
141  *
142  * @param pOldMem
143  * Pointer to the old block of memory.
144  * If this parameter is NULL, ReallocSplMem behaves exactly like DllAllocSplMem.
145  *
146  * @param cbOld
147  * Number of bytes to copy from the old block into the new one.
148  *
149  * @param cbNew
150  * Number of bytes to allocate for the new block.
151  *
152  * @return
153  * A pointer to the allocated new block or NULL in case of an error.
154  * You have to free this memory using DllFreeSplMem.
155  */
156 PVOID WINAPI
157 ReallocSplMem(PVOID pOldMem, DWORD cbOld, DWORD cbNew)
158 {
159     PVOID pNewMem;
160 
161     // Always allocate the new block of memory.
162     pNewMem = DllAllocSplMem(cbNew);
163     if (!pNewMem)
164     {
165         ERR("DllAllocSplMem failed!\n");
166         return NULL;
167     }
168 
169     // Copy the old memory into the new block and free it.
170     if (pOldMem)
171     {
172         CopyMemory(pNewMem, pOldMem, min(cbOld, cbNew));
173         DllFreeSplMem(pOldMem);
174     }
175 
176     return pNewMem;
177 }
178 
179 /**
180  * @name ReallocSplStr
181  *
182  * Frees a string allocated by AllocSplStr and copies the given Unicode string into a newly allocated block of memory.
183  *
184  * @param ppwszString
185  * Pointer to the string pointer allocated by AllocSplStr.
186  * When the function returns, the variable receives the pointer to the copied string.
187  *
188  * @param pwszInput
189  * The Unicode string to copy into the new block of memory.
190  *
191  * @return
192  * Returns TRUE in any case.
193 */
194 BOOL WINAPI
195 ReallocSplStr(PWSTR* ppwszString, PCWSTR pwszInput)
196 {
197     if (*ppwszString)
198         DllFreeSplStr(*ppwszString);
199 
200     *ppwszString = AllocSplStr(pwszInput);
201 
202     return TRUE;
203 }
204 
205 /**
206  * @name UndoAlignRpcPtr
207  *
208  * Copies the data from the aligned buffer previously allocated by AlignRpcPtr back to the original unaligned buffer.
209  * The aligned buffer is freed.
210  *
211  * Also aligns up the returned required buffer size of a function to a 4-byte boundary.
212  *
213  * @param pDestinationBuffer
214  * The original unaligned buffer, which you input as pBuffer to AlignRpcPtr.
215  * The data from pSourceBuffer is copied into this buffer before pSourceBuffer is freed.
216  * If AlignRpcPtr did not allocate a buffer, pDestinationBuffer equals pSourceBuffer and no memory is copied or freed.
217  * This parameter may be NULL if pSourceBuffer is NULL or cbBuffer is 0.
218  *
219  * @param pSourceBuffer
220  * The aligned buffer, which is returned by AlignRpcPtr.
221  * Its data is copied into pDestinationBuffer and then pSourceBuffer is freed.
222  * If AlignRpcPtr did not allocate a buffer, pDestinationBuffer equals pSourceBuffer and no memory is copied or freed.
223  * This parameter may be NULL.
224  *
225  * @param cbBuffer
226  * Number of bytes to copy.
227  * Set this to the size returned by AlignRpcPtr's pcbBuffer or less.
228  *
229  * @param pcbNeeded
230  * Let this parameter point to your variable calculating the needed bytes for a buffer and returning this value to the user.
231  * It is then aligned up to a 4-byte boundary, so that the user supplies a large enough buffer in the next call.
232  * Otherwise, AlignRpcPtr would align down the buffer size in the next call and your buffer would be smaller than intended.
233  * This parameter may be NULL.
234  *
235  * @return
236  * pcbNeeded
237  */
238 PDWORD WINAPI
239 UndoAlignRpcPtr(PVOID pDestinationBuffer, PVOID pSourceBuffer, DWORD cbBuffer, PDWORD pcbNeeded)
240 {
241     // pDestinationBuffer is accessed unless pSourceBuffer equals pDestinationBuffer or cbBuffer is 0.
242     ASSERT(pDestinationBuffer || pSourceBuffer == pDestinationBuffer || cbBuffer == 0);
243 
244     // If pSourceBuffer is given, and source and destination pointers don't match,
245     // we assume that pSourceBuffer is the buffer allocated by AlignRpcPtr.
246     if (pSourceBuffer && pSourceBuffer != pDestinationBuffer)
247     {
248         // Copy back the buffer data to the (usually unaligned) destination buffer
249         // and free the buffer allocated by AlignRpcPtr.
250         CopyMemory(pDestinationBuffer, pSourceBuffer, cbBuffer);
251         DllFreeSplMem(pSourceBuffer);
252     }
253 
254     // If pcbNeeded is given, align it up to a 4-byte boundary.
255     if (pcbNeeded && *pcbNeeded % sizeof(DWORD))
256         *pcbNeeded += sizeof(DWORD) - *pcbNeeded % sizeof(DWORD);
257 
258     return pcbNeeded;
259 }
260