2 * Dynamic pointer array (DPA) implementation
4 * Copyright 1998 Eric Kohl
5 * 1998 Juergen Schmied <j.schmied@metronet.de>
6 * 2000 Eric Kohl for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * These functions were involuntarily documented by Microsoft in 2002 as
24 * the outcome of an anti-trust suit brought by various U.S. governments.
25 * As a result the specifications on MSDN are inaccurate, incomplete
26 * and misleading. A much more complete (unofficial) documentation is
29 * http://members.ozemail.com.au/~geoffch/samples/win32/shell/comctl32
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(dpa
);
57 typedef struct _STREAMDATA
62 } STREAMDATA
, *PSTREAMDATA
;
64 typedef struct _LOADDATA
68 } LOADDATA
, *LPLOADDATA
;
70 typedef HRESULT (CALLBACK
*DPALOADPROC
)(LPLOADDATA
,IStream
*,LPARAM
);
73 /**************************************************************************
74 * DPA_LoadStream [COMCTL32.9]
76 * Loads a dynamic pointer array from a stream
79 * phDpa [O] pointer to a handle to a dynamic pointer array
80 * loadProc [I] pointer to a callback function
81 * pStream [I] pointer to a stream
82 * lParam [I] application specific value
89 * No more information available yet!
91 HRESULT WINAPI
DPA_LoadStream (HDPA
*phDpa
, DPALOADPROC loadProc
,
92 IStream
*pStream
, LPARAM lParam
)
95 LARGE_INTEGER position
;
96 ULARGE_INTEGER newPosition
;
97 STREAMDATA streamData
;
103 FIXME ("phDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
104 phDpa
, loadProc
, pStream
, lParam
);
106 if (!phDpa
|| !loadProc
|| !pStream
)
111 position
.QuadPart
= 0;
114 * Zero out our streamData
116 memset(&streamData
,0,sizeof(STREAMDATA
));
118 errCode
= IStream_Seek (pStream
, position
, STREAM_SEEK_CUR
, &newPosition
);
122 errCode
= IStream_Read (pStream
, &streamData
, sizeof(STREAMDATA
), &ulRead
);
126 FIXME ("dwSize=%lu dwData2=%lu dwItems=%lu\n",
127 streamData
.dwSize
, streamData
.dwData2
, streamData
.dwItems
);
129 if ( ulRead
< sizeof(STREAMDATA
) ||
130 lParam
< sizeof(STREAMDATA
) ||
131 streamData
.dwSize
< sizeof(STREAMDATA
) ||
132 streamData
.dwData2
< 1) {
136 if (streamData
.dwItems
> (UINT_MAX
/ 2 / sizeof(VOID
*))) /* 536870911 */
137 return E_OUTOFMEMORY
;
140 hDpa
= DPA_Create (streamData
.dwItems
);
142 return E_OUTOFMEMORY
;
144 if (!DPA_Grow (hDpa
, streamData
.dwItems
))
145 return E_OUTOFMEMORY
;
147 /* load data from the stream into the dpa */
149 for (loadData
.nCount
= 0; loadData
.nCount
< streamData
.dwItems
; loadData
.nCount
++) {
150 errCode
= (loadProc
)(&loadData
, pStream
, lParam
);
151 if (errCode
!= S_OK
) {
160 /* set the number of items */
161 hDpa
->nItemCount
= loadData
.nCount
;
163 /* store the handle to the dpa */
165 FIXME ("new hDpa=%p, errorcode=%lx\n", hDpa
, errCode
);
171 /**************************************************************************
172 * DPA_SaveStream [COMCTL32.10]
174 * Saves a dynamic pointer array to a stream
177 * hDpa [I] handle to a dynamic pointer array
178 * loadProc [I] pointer to a callback function
179 * pStream [I] pointer to a stream
180 * lParam [I] application specific value
187 * No more information available yet!
189 HRESULT WINAPI
DPA_SaveStream (const HDPA hDpa
, DPALOADPROC loadProc
,
190 IStream
*pStream
, LPARAM lParam
)
193 FIXME ("hDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
194 hDpa
, loadProc
, pStream
, lParam
);
200 /**************************************************************************
201 * DPA_Merge [COMCTL32.11]
203 * Merge two dynamic pointers arrays.
206 * hdpa1 [I] handle to a dynamic pointer array
207 * hdpa2 [I] handle to a dynamic pointer array
209 * pfnCompare [I] pointer to sort function
210 * pfnMerge [I] pointer to merge function
211 * lParam [I] application specific value
218 * No more information available yet!
220 BOOL WINAPI
DPA_Merge (const HDPA hdpa1
, const HDPA hdpa2
, DWORD dwFlags
,
221 PFNDPACOMPARE pfnCompare
, PFNDPAMERGE pfnMerge
,
225 LPVOID
*pWork1
, *pWork2
;
229 TRACE("%p %p %08lx %p %p %08lx)\n",
230 hdpa1
, hdpa2
, dwFlags
, pfnCompare
, pfnMerge
, lParam
);
232 if (IsBadWritePtr (hdpa1
, sizeof(*hdpa1
)))
235 if (IsBadWritePtr (hdpa2
, sizeof(*hdpa2
)))
238 if (IsBadCodePtr ((FARPROC
)pfnCompare
))
241 if (IsBadCodePtr ((FARPROC
)pfnMerge
))
244 if (!(dwFlags
& DPAM_NOSORT
)) {
245 TRACE("sorting dpa's!\n");
246 if (hdpa1
->nItemCount
> 0)
247 DPA_Sort (hdpa1
, pfnCompare
, lParam
);
248 TRACE ("dpa 1 sorted!\n");
249 if (hdpa2
->nItemCount
> 0)
250 DPA_Sort (hdpa2
, pfnCompare
, lParam
);
251 TRACE ("dpa 2 sorted!\n");
254 if (hdpa2
->nItemCount
< 1)
257 TRACE("hdpa1->nItemCount=%d hdpa2->nItemCount=%d\n",
258 hdpa1
->nItemCount
, hdpa2
->nItemCount
);
261 /* working but untrusted implementation */
263 pWork1
= &(hdpa1
->ptrs
[hdpa1
->nItemCount
- 1]);
264 pWork2
= &(hdpa2
->ptrs
[hdpa2
->nItemCount
- 1]);
266 nIndex
= hdpa1
->nItemCount
- 1;
267 nCount
= hdpa2
->nItemCount
- 1;
272 if ((nCount
>= 0) && (dwFlags
& DPAM_INSERT
)) {
273 /* Now insert the remaining new items into DPA 1 */
274 TRACE("%d items to be inserted at start of DPA 1\n",
276 for (i
=nCount
; i
>=0; i
--) {
279 ptr
= (pfnMerge
)(3, *pWork2
, NULL
, lParam
);
282 DPA_InsertPtr (hdpa1
, 0, ptr
);
288 nResult
= (pfnCompare
)(*pWork1
, *pWork2
, lParam
);
289 TRACE("compare result=%d, dpa1.cnt=%d, dpa2.cnt=%d\n",
290 nResult
, nIndex
, nCount
);
296 ptr
= (pfnMerge
)(1, *pWork1
, *pWork2
, lParam
);
306 else if (nResult
> 0)
308 /* item in DPA 1 missing from DPA 2 */
309 if (dwFlags
& DPAM_DELETE
)
311 /* Now delete the extra item in DPA1 */
314 ptr
= DPA_DeletePtr (hdpa1
, hdpa1
->nItemCount
- 1);
316 (pfnMerge
)(2, ptr
, NULL
, lParam
);
323 /* new item in DPA 2 */
324 if (dwFlags
& DPAM_INSERT
)
326 /* Now insert the new item in DPA 1 */
329 ptr
= (pfnMerge
)(3, *pWork2
, NULL
, lParam
);
332 DPA_InsertPtr (hdpa1
, nIndex
+1, ptr
);
345 /**************************************************************************
346 * DPA_Destroy [COMCTL32.329]
348 * Destroys a dynamic pointer array
351 * hdpa [I] handle (pointer) to the pointer array
357 BOOL WINAPI
DPA_Destroy (const HDPA hdpa
)
359 TRACE("(%p)\n", hdpa
);
364 if (hdpa
->ptrs
&& (!HeapFree (hdpa
->hHeap
, 0, hdpa
->ptrs
)))
367 return HeapFree (hdpa
->hHeap
, 0, hdpa
);
371 /**************************************************************************
372 * DPA_Grow [COMCTL32.330]
374 * Sets the growth amount.
377 * hdpa [I] handle (pointer) to the existing (source) pointer array
378 * nGrow [I] number of items by which the array grows when it's too small
384 BOOL WINAPI
DPA_Grow (const HDPA hdpa
, INT nGrow
)
386 TRACE("(%p %d)\n", hdpa
, nGrow
);
391 hdpa
->nGrow
= max(8, nGrow
);
397 /**************************************************************************
398 * DPA_Clone [COMCTL32.331]
400 * Copies a pointer array to an other one or creates a copy
403 * hdpa [I] handle (pointer) to the existing (source) pointer array
404 * hdpaNew [O] handle (pointer) to the destination pointer array
407 * Success: pointer to the destination pointer array.
411 * - If the 'hdpaNew' is a NULL-Pointer, a copy of the source pointer
412 * array will be created and it's handle (pointer) is returned.
413 * - If 'hdpa' is a NULL-Pointer, the original implementation crashes,
414 * this implementation just returns NULL.
416 HDPA WINAPI
DPA_Clone (const HDPA hdpa
, const HDPA hdpaNew
)
418 INT nNewItems
, nSize
;
424 TRACE("(%p %p)\n", hdpa
, hdpaNew
);
427 /* create a new DPA */
428 hdpaTemp
= HeapAlloc (hdpa
->hHeap
, HEAP_ZERO_MEMORY
,
430 hdpaTemp
->hHeap
= hdpa
->hHeap
;
431 hdpaTemp
->nGrow
= hdpa
->nGrow
;
436 if (hdpaTemp
->ptrs
) {
437 /* remove old pointer array */
438 HeapFree (hdpaTemp
->hHeap
, 0, hdpaTemp
->ptrs
);
439 hdpaTemp
->ptrs
= NULL
;
440 hdpaTemp
->nItemCount
= 0;
441 hdpaTemp
->nMaxCount
= 0;
444 /* create a new pointer array */
445 nNewItems
= hdpaTemp
->nGrow
*
446 ((INT
)((hdpa
->nItemCount
- 1) / hdpaTemp
->nGrow
) + 1);
447 nSize
= nNewItems
* sizeof(LPVOID
);
448 hdpaTemp
->ptrs
= HeapAlloc (hdpaTemp
->hHeap
, HEAP_ZERO_MEMORY
, nSize
);
449 hdpaTemp
->nMaxCount
= nNewItems
;
451 /* clone the pointer array */
452 hdpaTemp
->nItemCount
= hdpa
->nItemCount
;
453 memmove (hdpaTemp
->ptrs
, hdpa
->ptrs
,
454 hdpaTemp
->nItemCount
* sizeof(LPVOID
));
460 /**************************************************************************
461 * DPA_GetPtr [COMCTL32.332]
463 * Retrieves a pointer from a dynamic pointer array
466 * hdpa [I] handle (pointer) to the pointer array
467 * nIndex [I] array index of the desired pointer
473 LPVOID WINAPI
DPA_GetPtr (const HDPA hdpa
, INT nIndex
)
475 TRACE("(%p %d)\n", hdpa
, nIndex
);
480 WARN("no pointer array.\n");
483 if ((nIndex
< 0) || (nIndex
>= hdpa
->nItemCount
)) {
484 WARN("not enough pointers in array (%d vs %d).\n",nIndex
,hdpa
->nItemCount
);
488 TRACE("-- %p\n", hdpa
->ptrs
[nIndex
]);
490 return hdpa
->ptrs
[nIndex
];
494 /**************************************************************************
495 * DPA_GetPtrIndex [COMCTL32.333]
497 * Retrieves the index of the specified pointer
500 * hdpa [I] handle (pointer) to the pointer array
504 * Success: index of the specified pointer
507 INT WINAPI
DPA_GetPtrIndex (const HDPA hdpa
, LPVOID p
)
511 if (!hdpa
|| !hdpa
->ptrs
)
514 for (i
= 0; i
< hdpa
->nItemCount
; i
++) {
515 if (hdpa
->ptrs
[i
] == p
)
523 /**************************************************************************
524 * DPA_InsertPtr [COMCTL32.334]
526 * Inserts a pointer into a dynamic pointer array
529 * hdpa [I] handle (pointer) to the array
531 * p [I] pointer to insert
534 * Success: index of the inserted pointer
537 INT WINAPI
DPA_InsertPtr (const HDPA hdpa
, INT i
, LPVOID p
)
539 TRACE("(%p %d %p)\n", hdpa
, i
, p
);
541 if (!hdpa
|| i
< 0) return -1;
543 /* append item if index is out of bounds */
544 i
= min(hdpa
->nItemCount
, i
);
546 /* create empty spot at the end */
547 if (!DPA_SetPtr(hdpa
, hdpa
->nItemCount
, 0)) return -1;
549 if (i
!= hdpa
->nItemCount
- 1)
550 memmove (hdpa
->ptrs
+ i
+ 1, hdpa
->ptrs
+ i
,
551 (hdpa
->nItemCount
- i
- 1) * sizeof(LPVOID
));
558 /**************************************************************************
559 * DPA_SetPtr [COMCTL32.335]
561 * Sets a pointer in the pointer array
564 * hdpa [I] handle (pointer) to the pointer array
565 * i [I] index of the pointer that will be set
566 * p [I] pointer to be set
572 BOOL WINAPI
DPA_SetPtr (const HDPA hdpa
, INT i
, LPVOID p
)
576 TRACE("(%p %d %p)\n", hdpa
, i
, p
);
581 if (hdpa
->nItemCount
<= i
) {
582 /* within the old array */
583 if (hdpa
->nMaxCount
<= i
) {
584 /* resize the block of memory */
586 hdpa
->nGrow
* ((INT
)(((i
+1) - 1) / hdpa
->nGrow
) + 1);
587 INT nSize
= nNewItems
* sizeof(LPVOID
);
590 lpTemp
= HeapReAlloc (hdpa
->hHeap
, HEAP_ZERO_MEMORY
, hdpa
->ptrs
, nSize
);
592 lpTemp
= HeapAlloc (hdpa
->hHeap
, HEAP_ZERO_MEMORY
, nSize
);
597 hdpa
->nMaxCount
= nNewItems
;
600 hdpa
->nItemCount
= i
+1;
603 /* put the new entry in */
610 /**************************************************************************
611 * DPA_DeletePtr [COMCTL32.336]
613 * Removes a pointer from the pointer array.
616 * hdpa [I] handle (pointer) to the pointer array
617 * i [I] index of the pointer that will be deleted
620 * Success: deleted pointer
623 LPVOID WINAPI
DPA_DeletePtr (const HDPA hdpa
, INT i
)
625 LPVOID
*lpDest
, *lpSrc
, lpTemp
= NULL
;
628 TRACE("(%p %d)\n", hdpa
, i
);
630 if ((!hdpa
) || i
< 0 || i
>= hdpa
->nItemCount
)
633 lpTemp
= hdpa
->ptrs
[i
];
635 /* do we need to move ?*/
636 if (i
< hdpa
->nItemCount
- 1) {
637 lpDest
= hdpa
->ptrs
+ i
;
639 nSize
= (hdpa
->nItemCount
- i
- 1) * sizeof(LPVOID
);
640 TRACE("-- move dest=%p src=%p size=%x\n",
641 lpDest
, lpSrc
, nSize
);
642 memmove (lpDest
, lpSrc
, nSize
);
648 if ((hdpa
->nMaxCount
- hdpa
->nItemCount
) >= hdpa
->nGrow
) {
649 INT nNewItems
= max(hdpa
->nGrow
* 2, hdpa
->nItemCount
);
650 nSize
= nNewItems
* sizeof(LPVOID
);
651 lpDest
= HeapReAlloc (hdpa
->hHeap
, HEAP_ZERO_MEMORY
,
656 hdpa
->nMaxCount
= nNewItems
;
657 hdpa
->ptrs
= (LPVOID
*)lpDest
;
664 /**************************************************************************
665 * DPA_DeleteAllPtrs [COMCTL32.337]
667 * Removes all pointers and reinitializes the array.
670 * hdpa [I] handle (pointer) to the pointer array
676 BOOL WINAPI
DPA_DeleteAllPtrs (const HDPA hdpa
)
678 TRACE("(%p)\n", hdpa
);
683 if (hdpa
->ptrs
&& (!HeapFree (hdpa
->hHeap
, 0, hdpa
->ptrs
)))
686 hdpa
->nItemCount
= 0;
687 hdpa
->nMaxCount
= hdpa
->nGrow
* 2;
688 hdpa
->ptrs
= HeapAlloc (hdpa
->hHeap
, HEAP_ZERO_MEMORY
,
689 hdpa
->nMaxCount
* sizeof(LPVOID
));
695 /**************************************************************************
696 * DPA_QuickSort [Internal]
698 * Ordinary quicksort (used by DPA_Sort).
701 * lpPtrs [I] pointer to the pointer array
702 * l [I] index of the "left border" of the partition
703 * r [I] index of the "right border" of the partition
704 * pfnCompare [I] pointer to the compare function
705 * lParam [I] user defined value (3rd parameter in compare function)
710 static VOID
DPA_QuickSort (LPVOID
*lpPtrs
, INT l
, INT r
,
711 PFNDPACOMPARE pfnCompare
, LPARAM lParam
)
716 TRACE("l=%i r=%i\n", l
, r
);
718 if (l
==r
) /* one element is always sorted */
720 if (r
<l
) /* oops, got it in the wrong order */
722 DPA_QuickSort(lpPtrs
, r
, l
, pfnCompare
, lParam
);
725 m
= (l
+r
)/2; /* divide by two */
726 DPA_QuickSort(lpPtrs
, l
, m
, pfnCompare
, lParam
);
727 DPA_QuickSort(lpPtrs
, m
+1, r
, pfnCompare
, lParam
);
729 /* join the two sides */
730 while( (l
<=m
) && (m
<r
) )
732 if(pfnCompare(lpPtrs
[l
],lpPtrs
[m
+1],lParam
)>0)
735 memmove(&lpPtrs
[l
+1],&lpPtrs
[l
],(m
-l
+1)*sizeof(lpPtrs
[l
]));
745 /**************************************************************************
746 * DPA_Sort [COMCTL32.338]
748 * Sorts a pointer array using a user defined compare function
751 * hdpa [I] handle (pointer) to the pointer array
752 * pfnCompare [I] pointer to the compare function
753 * lParam [I] user defined value (3rd parameter of compare function)
759 BOOL WINAPI
DPA_Sort (const HDPA hdpa
, PFNDPACOMPARE pfnCompare
, LPARAM lParam
)
761 if (!hdpa
|| !pfnCompare
)
764 TRACE("(%p %p 0x%lx)\n", hdpa
, pfnCompare
, lParam
);
766 if ((hdpa
->nItemCount
> 1) && (hdpa
->ptrs
))
767 DPA_QuickSort (hdpa
->ptrs
, 0, hdpa
->nItemCount
- 1,
774 /**************************************************************************
775 * DPA_Search [COMCTL32.339]
777 * Searches a pointer array for a specified pointer
780 * hdpa [I] handle (pointer) to the pointer array
781 * pFind [I] pointer to search for
782 * nStart [I] start index
783 * pfnCompare [I] pointer to the compare function
784 * lParam [I] user defined value (3rd parameter of compare function)
785 * uOptions [I] search options
788 * Success: index of the pointer in the array.
791 INT WINAPI
DPA_Search (const HDPA hdpa
, LPVOID pFind
, INT nStart
,
792 PFNDPACOMPARE pfnCompare
, LPARAM lParam
, UINT uOptions
)
794 if (!hdpa
|| !pfnCompare
|| !pFind
)
797 TRACE("(%p %p %d %p 0x%08lx 0x%08x)\n",
798 hdpa
, pFind
, nStart
, pfnCompare
, lParam
, uOptions
);
800 if (uOptions
& DPAS_SORTED
) {
801 /* array is sorted --> use binary search */
805 l
= (nStart
== -1) ? 0 : nStart
;
806 r
= hdpa
->nItemCount
- 1;
810 n
= (pfnCompare
)(pFind
, lpPtr
[x
], lParam
);
818 if (uOptions
& (DPAS_INSERTBEFORE
|DPAS_INSERTAFTER
)) return l
;
821 /* array is not sorted --> use linear search */
825 nIndex
= (nStart
== -1)? 0 : nStart
;
827 for (; nIndex
< hdpa
->nItemCount
; nIndex
++) {
828 if ((pfnCompare
)(pFind
, lpPtr
[nIndex
], lParam
) == 0)
837 /**************************************************************************
838 * DPA_CreateEx [COMCTL32.340]
840 * Creates a dynamic pointer array using the specified size and heap.
843 * nGrow [I] number of items by which the array grows when it is filled
844 * hHeap [I] handle to the heap where the array is stored
847 * Success: handle (pointer) to the pointer array.
851 * The DPA_ functions can be used to create and manipulate arrays of
854 HDPA WINAPI
DPA_CreateEx (INT nGrow
, HANDLE hHeap
)
858 TRACE("(%d %p)\n", nGrow
, hHeap
);
861 hdpa
= HeapAlloc (hHeap
, HEAP_ZERO_MEMORY
, sizeof(*hdpa
));
863 hdpa
= Alloc (sizeof(*hdpa
));
866 hdpa
->nGrow
= max(8, nGrow
);
867 hdpa
->hHeap
= hHeap
? hHeap
: GetProcessHeap();
868 hdpa
->nMaxCount
= hdpa
->nGrow
* 2;
869 hdpa
->ptrs
= HeapAlloc (hdpa
->hHeap
, HEAP_ZERO_MEMORY
,
870 hdpa
->nMaxCount
* sizeof(LPVOID
));
873 TRACE("-- %p\n", hdpa
);
879 /**************************************************************************
880 * DPA_Create [COMCTL32.328]
882 * Creates a dynamic pointer array.
885 * nGrow [I] number of items by which the array grows when it is filled
888 * Success: handle (pointer) to the pointer array.
892 * The DPA_ functions can be used to create and manipulate arrays of
895 HDPA WINAPI
DPA_Create (INT nGrow
)
897 return DPA_CreateEx( nGrow
, 0 );
901 /**************************************************************************
902 * DPA_EnumCallback [COMCTL32.385]
904 * Enumerates all items in a dynamic pointer array.
907 * hdpa [I] handle to the dynamic pointer array
914 VOID WINAPI
DPA_EnumCallback (HDPA hdpa
, PFNDPAENUMCALLBACK enumProc
,
919 TRACE("(%p %p %p)\n", hdpa
, enumProc
, lParam
);
923 if (hdpa
->nItemCount
<= 0)
926 for (i
= 0; i
< hdpa
->nItemCount
; i
++) {
927 if ((enumProc
)(hdpa
->ptrs
[i
], lParam
) == 0)
935 /**************************************************************************
936 * DPA_DestroyCallback [COMCTL32.386]
938 * Enumerates all items in a dynamic pointer array and destroys it.
941 * hdpa [I] handle to the dynamic pointer array
948 void WINAPI
DPA_DestroyCallback (HDPA hdpa
, PFNDPAENUMCALLBACK enumProc
,
951 TRACE("(%p %p %p)\n", hdpa
, enumProc
, lParam
);
953 DPA_EnumCallback (hdpa
, enumProc
, lParam
);