[PATCH v3 4/4] comctl32/listbox: Implement LBS_NODATA for multi-selection listboxes

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed Feb 27 06:19:17 CST 2019


Use a byte array to store selection state of items, since we don't need any
other data for LBS_NODATA multi-selection listboxes. This improves memory
usage dramatically for large lists, and performance boosts are nice too.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/comctl32/listbox.c | 86 +++++++++++++++++++++++++++--------------
 1 file changed, 56 insertions(+), 30 deletions(-)

diff --git a/dlls/comctl32/listbox.c b/dlls/comctl32/listbox.c
index 46b2304..b19f482 100644
--- a/dlls/comctl32/listbox.c
+++ b/dlls/comctl32/listbox.c
@@ -18,8 +18,6 @@
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * TODO:
- *    - LBS_NODATA for multi-selection listboxes
  */
 
 #include <string.h>
@@ -71,7 +69,11 @@ typedef struct
     UINT        style;          /* Window style */
     INT         width;          /* Window width */
     INT         height;         /* Window height */
-    LB_ITEMDATA  *items;        /* Array of items */
+    union
+    {
+        LB_ITEMDATA *items;     /* Array of items */
+        BYTE *items_nodata;     /* For multi-selection LBS_NODATA */
+    } u;
     INT         nb_items;       /* Number of items */
     UINT        items_size;     /* Total number of allocated items in the array */
     INT         top_item;       /* Top visible item */
@@ -127,9 +129,19 @@ static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
 
 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
 
+/*
+   For listboxes without LBS_NODATA, an array of LB_ITEMDATA is allocated
+   to store the states of each item into descr->u.items.
+
+   For single-selection LBS_NODATA listboxes, no storage is allocated,
+   and thus descr->u.items_nodata will always be NULL.
+
+   For multi-selection LBS_NODATA listboxes, one byte per item is stored
+   for the item's selection state into descr->u.items_nodata.
+*/
 static size_t get_item_size( const LB_DESCR *descr )
 {
-    return sizeof(LB_ITEMDATA);
+    return (descr->style & LBS_NODATA) ? sizeof(BYTE) : sizeof(LB_ITEMDATA);
 }
 
 static BOOL resize_storage(LB_DESCR *descr, UINT items_size)
@@ -142,20 +154,20 @@ static BOOL resize_storage(LB_DESCR *descr, UINT items_size)
         items_size = (items_size + LB_ARRAY_GRANULARITY - 1) & ~(LB_ARRAY_GRANULARITY - 1);
         if ((descr->style & (LBS_NODATA | LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != LBS_NODATA)
         {
-            items = heap_realloc(descr->items, items_size * get_item_size(descr));
+            items = heap_realloc(descr->u.items, items_size * get_item_size(descr));
             if (!items)
             {
                 SEND_NOTIFICATION(descr, LBN_ERRSPACE);
                 return FALSE;
             }
-            descr->items = items;
+            descr->u.items = items;
         }
         descr->items_size = items_size;
     }
 
-    if ((descr->style & LBS_NODATA) && descr->items && items_size > descr->nb_items)
+    if ((descr->style & LBS_NODATA) && descr->u.items_nodata && items_size > descr->nb_items)
     {
-        memset(&descr->items[descr->nb_items], 0,
+        memset(&descr->u.items_nodata[descr->nb_items], 0,
                (items_size - descr->nb_items) * get_item_size(descr));
     }
     return TRUE;
@@ -163,67 +175,81 @@ static BOOL resize_storage(LB_DESCR *descr, UINT items_size)
 
 static ULONG_PTR get_item_data( const LB_DESCR *descr, UINT index )
 {
-    return (descr->style & LBS_NODATA) ? 0 : descr->items[index].data;
+    return (descr->style & LBS_NODATA) ? 0 : descr->u.items[index].data;
 }
 
 static void set_item_data( LB_DESCR *descr, UINT index, ULONG_PTR data )
 {
-    if (!(descr->style & LBS_NODATA)) descr->items[index].data = data;
+    if (!(descr->style & LBS_NODATA)) descr->u.items[index].data = data;
 }
 
 static WCHAR *get_item_string( const LB_DESCR *descr, UINT index )
 {
-    return HAS_STRINGS(descr) ? descr->items[index].str : NULL;
+    return HAS_STRINGS(descr) ? descr->u.items[index].str : NULL;
 }
 
 static UINT get_item_height( const LB_DESCR *descr, UINT index )
 {
-    return (descr->style & LBS_NODATA) ? 0 : descr->items[index].height;
+    return (descr->style & LBS_NODATA) ? 0 : descr->u.items[index].height;
 }
 
 static void set_item_height( LB_DESCR *descr, UINT index, UINT height )
 {
-    if (!(descr->style & LBS_NODATA)) descr->items[index].height = height;
+    if (!(descr->style & LBS_NODATA)) descr->u.items[index].height = height;
 }
 
 static BOOL is_item_selected( const LB_DESCR *descr, UINT index )
 {
     if (!(descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)))
         return index == descr->selected_item;
-    return descr->items[index].selected;
+    if (descr->style & LBS_NODATA)
+        return descr->u.items_nodata[index];
+    else
+        return descr->u.items[index].selected;
 }
 
 static void set_item_selected_state(LB_DESCR *descr, UINT index, BOOL state)
 {
     if (descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
-        descr->items[index].selected = state;
+    {
+        if (descr->style & LBS_NODATA)
+            descr->u.items_nodata[index] = state;
+        else
+            descr->u.items[index].selected = state;
+    }
 }
 
 static void insert_item_data(LB_DESCR *descr, UINT index, WCHAR *str, ULONG_PTR data)
 {
-    LB_ITEMDATA *item;
+    size_t size = get_item_size(descr);
+    BYTE *p = descr->u.items_nodata + index * size;
 
-    if (!descr->items) return;
+    if (!descr->u.items) return;
 
-    item = descr->items + index;
     if (index < descr->nb_items)
-        memmove(item + 1, item, (descr->nb_items - index) * get_item_size(descr));
+        memmove(p + size, p, (descr->nb_items - index) * size);
 
-    item->str      = str;
-    item->data     = HAS_STRINGS(descr) ? 0 : data;
-    item->height   = 0;
-    item->selected = FALSE;
+    if (descr->style & LBS_NODATA)
+        descr->u.items_nodata[index] = FALSE;
+    else
+    {
+        LB_ITEMDATA *item = descr->u.items + index;
+        item->str      = str;
+        item->data     = HAS_STRINGS(descr) ? 0 : data;
+        item->height   = 0;
+        item->selected = FALSE;
+    }
 }
 
 static void remove_item_data(LB_DESCR *descr, UINT index)
 {
-    LB_ITEMDATA *item;
+    size_t size = get_item_size(descr);
+    BYTE *p = descr->u.items_nodata + index * size;
 
-    if (!descr->items) return;
+    if (!descr->u.items) return;
 
-    item = descr->items + index;
     if (index < descr->nb_items)
-        memmove(item, item + 1, (descr->nb_items - index) * get_item_size(descr));
+        memmove(p, p + size, (descr->nb_items - index) * size);
 }
 
 /***********************************************************************
@@ -1776,14 +1802,14 @@ static void LISTBOX_ResetContent( LB_DESCR *descr )
 
     if (!(descr->style & LBS_NODATA))
         for (i = descr->nb_items - 1; i >= 0; i--) LISTBOX_DeleteItem(descr, i);
-    HeapFree( GetProcessHeap(), 0, descr->items );
+    HeapFree( GetProcessHeap(), 0, descr->u.items );
     descr->nb_items      = 0;
     descr->top_item      = 0;
     descr->selected_item = -1;
     descr->focus_item    = 0;
     descr->anchor_item   = -1;
     descr->items_size    = 0;
-    descr->items         = NULL;
+    descr->u.items       = NULL;
 }
 
 
@@ -2530,7 +2556,7 @@ static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
     descr->style         = GetWindowLongW( descr->self, GWL_STYLE );
     descr->width         = rect.right - rect.left;
     descr->height        = rect.bottom - rect.top;
-    descr->items         = NULL;
+    descr->u.items       = NULL;
     descr->items_size    = 0;
     descr->nb_items      = 0;
     descr->top_item      = 0;
-- 
2.20.1




More information about the wine-devel mailing list