* Sync up to trunk HEAD (r62285). Branch guys deserve the significant speedups too ;)
[reactos.git] / dll / win32 / shlwapi / clist.c
1 /*
2 * SHLWAPI DataBlock List functions
3 *
4 * Copyright 2002 Jon Griffiths
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "precomp.h"
22
23 /* dwSignature for contained DATABLOCK_HEADER items */
24 #define CLIST_ID_CONTAINER (~0U)
25
26 /*************************************************************************
27 * NextItem
28 *
29 * Internal helper: move a DataBlock pointer to the next item.
30 */
31 static inline LPDATABLOCK_HEADER NextItem(LPDBLIST lpList)
32 {
33 char* address = (char*)lpList;
34 address += lpList->cbSize;
35 return (LPDATABLOCK_HEADER)address;
36 }
37
38 /*************************************************************************
39 * @ [SHLWAPI.20]
40 *
41 * Insert a new item into a DataBlock list.
42 *
43 * PARAMS
44 * lppList [0] Pointer to the List
45 * lpNewItem [I] The new item to add to the list
46 *
47 * RETURNS
48 * Success: S_OK. The item is added to the list.
49 * Failure: An HRESULT error code.
50 *
51 * NOTES
52 * If the size of the element to be inserted is less than the size of a
53 * DATABLOCK_HEADER node, or the Id for the item is CLIST_ID_CONTAINER,
54 * the call returns S_OK but does not actually add the element.
55 * See SHWriteDataBlockList.
56 */
57 HRESULT WINAPI SHAddDataBlock(LPDBLIST* lppList, const DATABLOCK_HEADER *lpNewItem)
58 {
59 LPDATABLOCK_HEADER lpInsertAt = NULL;
60 ULONG ulSize;
61
62 TRACE("(%p,%p)\n", lppList, lpNewItem);
63
64 if(!lppList || !lpNewItem )
65 return E_INVALIDARG;
66
67 if (lpNewItem->cbSize < sizeof(DATABLOCK_HEADER) ||
68 lpNewItem->dwSignature == CLIST_ID_CONTAINER)
69 return S_OK;
70
71 ulSize = lpNewItem->cbSize;
72
73 if(ulSize & 0x3)
74 {
75 /* Tune size to a ULONG boundary, add space for container element */
76 ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(DATABLOCK_HEADER);
77 TRACE("Creating container item, new size = %d\n", ulSize);
78 }
79
80 if(!*lppList)
81 {
82 /* An empty list. Allocate space for terminal ulSize also */
83 *lppList = LocalAlloc(LMEM_ZEROINIT, ulSize + sizeof(ULONG));
84 lpInsertAt = *lppList;
85 }
86 else
87 {
88 /* Append to the end of the list */
89 ULONG ulTotalSize = 0;
90 LPDATABLOCK_HEADER lpIter = *lppList;
91
92 /* Iterate to the end of the list, calculating the total size */
93 while (lpIter->cbSize)
94 {
95 ulTotalSize += lpIter->cbSize;
96 lpIter = NextItem(lpIter);
97 }
98
99 /* Increase the size of the list */
100 lpIter = LocalReAlloc(*lppList, ulTotalSize + ulSize+sizeof(ULONG),
101 LMEM_ZEROINIT | LMEM_MOVEABLE);
102 if(lpIter)
103 {
104 *lppList = lpIter;
105 lpInsertAt = (LPDATABLOCK_HEADER)((char*)lpIter + ulTotalSize); /* At end */
106 }
107 }
108
109 if(lpInsertAt)
110 {
111 /* Copy in the new item */
112 LPDATABLOCK_HEADER lpDest = lpInsertAt;
113
114 if(ulSize != lpNewItem->cbSize)
115 {
116 lpInsertAt->cbSize = ulSize;
117 lpInsertAt->dwSignature = CLIST_ID_CONTAINER;
118 lpDest++;
119 }
120 memcpy(lpDest, lpNewItem, lpNewItem->cbSize);
121
122 /* Terminate the list */
123 lpInsertAt = NextItem(lpInsertAt);
124 lpInsertAt->cbSize = 0;
125
126 return lpNewItem->cbSize;
127 }
128 return S_OK;
129 }
130
131 /*************************************************************************
132 * @ [SHLWAPI.17]
133 *
134 * Write a DataBlock list to an IStream object.
135 *
136 * PARAMS
137 * lpStream [I] IStream object to write the list to
138 * lpList [I] List of items to write
139 *
140 * RETURNS
141 * Success: S_OK. The object is written to the stream.
142 * Failure: An HRESULT error code
143 *
144 * NOTES
145 * Ordinals 17,18,19,20,21 and 22 are related and together provide a compact
146 * list structure (a "DataBlock List"), which may be stored and retrieved from
147 * an IStream object.
148 *
149 * The exposed API consists of:
150 *
151 * - SHWriteDataBlockList() - Write a DataBlock list to a stream,
152 * - SHReadDataBlockList() - Read and create a list from a stream,
153 * - SHFreeDataBlockList() - Free a list,
154 * - SHAddDataBlock() - Insert a new item into a list,
155 * - SHRemoveDataBlock() - Remove an item from a list,
156 * - SHFindDataBlock() - Find an item in a list.
157 *
158 * The DataBlock list is stored packed into a memory array. Each element has a
159 * size and an associated ID. Elements must be less than 64k if the list is
160 * to be subsequently read from a stream.
161 *
162 * Elements are aligned on DWORD boundaries. If an elements data size is not
163 * a DWORD size multiple, the element is wrapped by inserting a surrounding
164 * element with an Id of 0xFFFFFFFF, and size sufficient to pad to a DWORD boundary.
165 *
166 * These functions are slow for large objects and long lists.
167 */
168 HRESULT WINAPI SHWriteDataBlockList(IStream* lpStream, LPDBLIST lpList)
169 {
170 ULONG ulSize;
171 HRESULT hRet = S_OK;
172
173 TRACE("(%p,%p)\n", lpStream, lpList);
174
175 if(lpList)
176 {
177 while (lpList->cbSize)
178 {
179 LPDATABLOCK_HEADER lpItem = lpList;
180
181 if(lpList->dwSignature == CLIST_ID_CONTAINER)
182 lpItem++;
183
184 hRet = IStream_Write(lpStream,lpItem,lpItem->cbSize,&ulSize);
185 if (FAILED(hRet))
186 return hRet;
187
188 if(lpItem->cbSize != ulSize)
189 return STG_E_MEDIUMFULL;
190
191 lpList = NextItem(lpList);
192 }
193 }
194
195 if(SUCCEEDED(hRet))
196 {
197 ULONG ulDummy;
198 ulSize = 0;
199
200 /* Write a terminating list entry with zero size */
201 hRet = IStream_Write(lpStream, &ulSize,sizeof(ulSize),&ulDummy);
202 }
203
204 return hRet;
205 }
206
207 /*************************************************************************
208 * @ [SHLWAPI.18]
209 *
210 * Read and create a DataBlock list from an IStream object.
211 *
212 * PARAMS
213 * lpStream [I] Stream to read the list from
214 * lppList [0] Pointer to receive the new List
215 *
216 * RETURNS
217 * Success: S_OK
218 * Failure: An HRESULT error code
219 *
220 * NOTES
221 * When read from a file, list objects are limited in size to 64k.
222 * See SHWriteDataBlockList.
223 */
224 HRESULT WINAPI SHReadDataBlockList(IStream* lpStream, LPDBLIST* lppList)
225 {
226 DATABLOCK_HEADER bBuff[128]; /* Temporary storage for new list item */
227 ULONG ulBuffSize = sizeof(bBuff);
228 LPDATABLOCK_HEADER pItem = bBuff;
229 ULONG ulRead, ulSize;
230 HRESULT hRet = S_OK;
231
232 TRACE("(%p,%p)\n", lpStream, lppList);
233
234 if(*lppList)
235 {
236 /* Free any existing list */
237 LocalFree(*lppList);
238 *lppList = NULL;
239 }
240
241 do
242 {
243 /* Read the size of the next item */
244 hRet = IStream_Read(lpStream, &ulSize,sizeof(ulSize),&ulRead);
245
246 if(FAILED(hRet) || ulRead != sizeof(ulSize) || !ulSize)
247 break; /* Read failed or read zero size (the end of the list) */
248
249 if(ulSize > 0xFFFF)
250 {
251 LARGE_INTEGER liZero;
252 ULARGE_INTEGER ulPos;
253
254 liZero.QuadPart = 0;
255
256 /* Back the stream up; this object is too big for the list */
257 if(SUCCEEDED(IStream_Seek(lpStream, liZero, STREAM_SEEK_CUR, &ulPos)))
258 {
259 liZero.QuadPart = ulPos.QuadPart - sizeof(ULONG);
260 IStream_Seek(lpStream, liZero, STREAM_SEEK_SET, NULL);
261 }
262 break;
263 }
264 else if (ulSize >= sizeof(DATABLOCK_HEADER))
265 {
266 /* Add this new item to the list */
267 if(ulSize > ulBuffSize)
268 {
269 /* We need more buffer space, allocate it */
270 LPDATABLOCK_HEADER lpTemp;
271
272 if (pItem == bBuff)
273 lpTemp = LocalAlloc(LMEM_ZEROINIT, ulSize);
274 else
275 lpTemp = LocalReAlloc(pItem, ulSize, LMEM_ZEROINIT|LMEM_MOVEABLE);
276
277 if(!lpTemp)
278 {
279 hRet = E_OUTOFMEMORY;
280 break;
281 }
282 ulBuffSize = ulSize;
283 pItem = lpTemp;
284 }
285
286 pItem->cbSize = ulSize;
287 ulSize -= sizeof(pItem->cbSize); /* already read this member */
288
289 /* Read the item Id and data */
290 hRet = IStream_Read(lpStream, &pItem->dwSignature, ulSize, &ulRead);
291
292 if(FAILED(hRet) || ulRead != ulSize)
293 break;
294
295 SHAddDataBlock(lppList, pItem); /* Insert Item */
296 }
297 } while(1);
298
299 /* If we allocated space, free it */
300 if(pItem != bBuff)
301 LocalFree(pItem);
302
303 return hRet;
304 }
305
306 /*************************************************************************
307 * @ [SHLWAPI.19]
308 *
309 * Free a DataBlock list.
310 *
311 * PARAMS
312 * lpList [I] List to free
313 *
314 * RETURNS
315 * Nothing.
316 *
317 * NOTES
318 * See SHWriteDataBlockList.
319 */
320 VOID WINAPI SHFreeDataBlockList(LPDBLIST lpList)
321 {
322 TRACE("(%p)\n", lpList);
323
324 if (lpList)
325 LocalFree(lpList);
326 }
327
328 /*************************************************************************
329 * @ [SHLWAPI.21]
330 *
331 * Remove an item from a DataBlock list.
332 *
333 * PARAMS
334 * lppList [O] List to remove the item from
335 * dwSignature [I] Id of item to remove
336 *
337 * RETURNS
338 * Success: TRUE.
339 * Failure: FALSE, If any parameters are invalid, or the item was not found.
340 *
341 * NOTES
342 * See SHWriteDataBlockList.
343 */
344 BOOL WINAPI SHRemoveDataBlock(LPDBLIST* lppList, DWORD dwSignature)
345 {
346 LPDATABLOCK_HEADER lpList = 0;
347 LPDATABLOCK_HEADER lpItem = NULL;
348 LPDATABLOCK_HEADER lpNext;
349 ULONG ulNewSize;
350
351 TRACE("(%p,%d)\n", lppList, dwSignature);
352
353 if(lppList && (lpList = *lppList))
354 {
355 /* Search for item in list */
356 while (lpList->cbSize)
357 {
358 if(lpList->dwSignature == dwSignature ||
359 (lpList->dwSignature == CLIST_ID_CONTAINER && lpList[1].dwSignature == dwSignature))
360 {
361 lpItem = lpList; /* Found */
362 break;
363 }
364 lpList = NextItem(lpList);
365 }
366 }
367
368 if(!lpItem)
369 return FALSE;
370
371 lpList = lpNext = NextItem(lpItem);
372
373 /* Locate the end of the list */
374 while (lpList->cbSize)
375 lpList = NextItem(lpList);
376
377 /* Resize the list */
378 ulNewSize = LocalSize(*lppList) - lpItem->cbSize;
379
380 /* Copy following elements over lpItem */
381 memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG));
382
383 if(ulNewSize <= sizeof(ULONG))
384 {
385 LocalFree(*lppList);
386 *lppList = NULL; /* Removed the last element */
387 }
388 else
389 {
390 lpList = LocalReAlloc(*lppList, ulNewSize, LMEM_ZEROINIT|LMEM_MOVEABLE);
391 if(lpList)
392 *lppList = lpList;
393 }
394 return TRUE;
395 }
396
397 /*************************************************************************
398 * @ [SHLWAPI.22]
399 *
400 * Find an item in a DataBlock list.
401 *
402 * PARAMS
403 * lpList [I] List to search
404 * dwSignature [I] Id of item to find
405 *
406 * RETURNS
407 * Success: A pointer to the list item found
408 * Failure: NULL
409 *
410 * NOTES
411 * See SHWriteDataBlockList.
412 */
413 DATABLOCK_HEADER* WINAPI SHFindDataBlock(LPDBLIST lpList, DWORD dwSignature)
414 {
415 TRACE("(%p,%d)\n", lpList, dwSignature);
416
417 if(lpList)
418 {
419 while(lpList->cbSize)
420 {
421 if(lpList->dwSignature == dwSignature)
422 return lpList; /* Matched */
423 else if(lpList->dwSignature == CLIST_ID_CONTAINER && lpList[1].dwSignature == dwSignature)
424 return lpList + 1; /* Contained item matches */
425
426 lpList = NextItem(lpList);
427 }
428 }
429 return NULL;
430 }