]> git.saurik.com Git - wxWidgets.git/blame - src/common/windowid.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / windowid.cpp
CommitLineData
cf2810aa
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/common/windowid.cpp
3// Purpose: wxWindowID class - a class for managing window ids
4// Author: Brian Vanderburg II
5// Created: 2007-09-21
6// RCS-ID: $Id$
7// Copyright: (c) 2007 Brian Vanderburg II
8// Licence: wxWindows licence
9///////////////////////////////////////////////////////////////////////////////
10
11// ----------------------------------------------------------------------------
12// Needed headers
13// ----------------------------------------------------------------------------
14#include "wx/wxprec.h"
15
16#ifdef __BORLANDC__
17 #pragma hdrstop
18#endif
19
20#ifndef WX_PRECOMP
21 #include "wx/log.h"
22 #include "wx/intl.h"
23#endif //WX_PRECOMP
24
535a0e08
VZ
25#include "wx/hashmap.h"
26
cf2810aa
VZ
27// Not needed, included in defs.h
28// #include "wx/windowid.h"
29
9a83f860 30#define wxTRACE_WINDOWID wxT("windowid")
cf2810aa
VZ
31
32namespace
33{
34
35#if wxUSE_AUTOID_MANAGEMENT
36
37
38// initially no ids are in use and we allocate them consecutively, but after we
39// exhaust the entire range, we wrap around and reuse the ids freed in the
40// meanwhile
41static const wxUint8 ID_FREE = 0;
42static const wxUint8 ID_STARTCOUNT = 1;
535a0e08 43static const wxUint8 ID_COUNTTOOLARGE = 254;
cf2810aa
VZ
44static const wxUint8 ID_RESERVED = 255;
45
535a0e08
VZ
46// we use a two level count, most IDs will be used less than ID_COUNTTOOLARGE-1
47// thus we store their count directly in this array, however when the same ID
48// is reused a great number of times (more than or equal to ID_COUNTTOOLARGE),
49// the hash map stores the actual count
cf2810aa
VZ
50wxUint8 gs_autoIdsRefCount[wxID_AUTO_HIGHEST - wxID_AUTO_LOWEST + 1] = { 0 };
51
535a0e08
VZ
52// NB: this variable is allocated (again) only when an ID gets at least
53// ID_COUNTTOOLARGE refs, and is freed when the latest entry in the map gets
54// freed. The cell storing the count for an ID is freed only when its count
55// gets to zero (not when it goes below ID_COUNTTOOLARGE, so as to avoid
56// degenerate cases)
57wxLongToLongHashMap *gs_autoIdsLargeRefCount = NULL;
58
cf2810aa
VZ
59// this is an optimization used until we wrap around wxID_AUTO_HIGHEST: if this
60// value is < wxID_AUTO_HIGHEST we know that we haven't wrapped yet and so can
61// allocate the ids simply by incrementing it
62wxWindowID gs_nextAutoId = wxID_AUTO_LOWEST;
63
64// Reserve an ID
e3b2f973 65void ReserveIdRefCount(wxWindowID winid)
cf2810aa 66{
e3b2f973 67 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
68 wxT("invalid id range"));
69
e3b2f973 70 winid -= wxID_AUTO_LOWEST;
cf2810aa 71
e3b2f973 72 wxCHECK_RET(gs_autoIdsRefCount[winid] == ID_FREE,
cf2810aa 73 wxT("id already in use or already reserved"));
e3b2f973 74 gs_autoIdsRefCount[winid] = ID_RESERVED;
cf2810aa
VZ
75}
76
77// Unreserve and id
e3b2f973 78void UnreserveIdRefCount(wxWindowID winid)
cf2810aa 79{
e3b2f973 80 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
81 wxT("invalid id range"));
82
e3b2f973 83 winid -= wxID_AUTO_LOWEST;
cf2810aa 84
e3b2f973 85 wxCHECK_RET(gs_autoIdsRefCount[winid] == ID_RESERVED,
cf2810aa 86 wxT("id already in use or not reserved"));
e3b2f973 87 gs_autoIdsRefCount[winid] = ID_FREE;
cf2810aa
VZ
88}
89
90// Get the usage count of an id
e3b2f973 91int GetIdRefCount(wxWindowID winid)
cf2810aa 92{
e3b2f973 93 wxCHECK_MSG(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST, 0,
cf2810aa
VZ
94 wxT("invalid id range"));
95
e3b2f973 96 winid -= wxID_AUTO_LOWEST;
535a0e08
VZ
97 int refCount = gs_autoIdsRefCount[winid];
98 if (refCount == ID_COUNTTOOLARGE)
99 refCount = (*gs_autoIdsLargeRefCount)[winid];
100 return refCount;
cf2810aa
VZ
101}
102
103// Increase the count for an id
e3b2f973 104void IncIdRefCount(wxWindowID winid)
cf2810aa 105{
e3b2f973 106 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
107 wxT("invalid id range"));
108
e3b2f973 109 winid -= wxID_AUTO_LOWEST;
cf2810aa 110
e3b2f973 111 wxCHECK_RET(gs_autoIdsRefCount[winid] != ID_FREE, wxT("id should first be reserved"));
cf2810aa 112
e3b2f973 113 if(gs_autoIdsRefCount[winid] == ID_RESERVED)
535a0e08 114 {
e3b2f973 115 gs_autoIdsRefCount[winid] = ID_STARTCOUNT;
535a0e08
VZ
116 }
117 else if (gs_autoIdsRefCount[winid] >= ID_COUNTTOOLARGE-1)
118 {
119 if (gs_autoIdsRefCount[winid] == ID_COUNTTOOLARGE-1)
120 {
121 // we need to allocate a cell, and maybe the hash map itself
122 if (!gs_autoIdsLargeRefCount)
123 gs_autoIdsLargeRefCount = new wxLongToLongHashMap;
124 (*gs_autoIdsLargeRefCount)[winid] = ID_COUNTTOOLARGE-1;
125
126 gs_autoIdsRefCount[winid] = ID_COUNTTOOLARGE;
127 }
128 ++(*gs_autoIdsLargeRefCount)[winid];
129 }
cf2810aa 130 else
535a0e08 131 {
e3b2f973 132 gs_autoIdsRefCount[winid]++;
535a0e08 133 }
cf2810aa
VZ
134
135 wxLogTrace(wxTRACE_WINDOWID, wxT("Increasing ref count of ID %d to %d"),
535a0e08 136 winid + wxID_AUTO_LOWEST, GetIdRefCount(winid + wxID_AUTO_LOWEST));
cf2810aa
VZ
137}
138
139// Decrease the count for an id
e3b2f973 140void DecIdRefCount(wxWindowID winid)
cf2810aa 141{
e3b2f973 142 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
143 wxT("invalid id range"));
144
e3b2f973 145 winid -= wxID_AUTO_LOWEST;
cf2810aa 146
e3b2f973 147 wxCHECK_RET(gs_autoIdsRefCount[winid] != ID_FREE, wxT("id count already 0"));
cf2810aa
VZ
148
149 // DecIdRefCount is only called on an ID that has been IncIdRefCount'ed'
150 // so it should never be reserved, but test anyway
e3b2f973 151 if(gs_autoIdsRefCount[winid] == ID_RESERVED)
cf2810aa 152 {
e54266ad 153 wxFAIL_MSG(wxT("reserve id being decreased"));
e3b2f973 154 gs_autoIdsRefCount[winid] = ID_FREE;
cf2810aa 155 }
535a0e08
VZ
156 else if(gs_autoIdsRefCount[winid] == ID_COUNTTOOLARGE)
157 {
158 long &largeCount = (*gs_autoIdsLargeRefCount)[winid];
159 --largeCount;
160 if (largeCount == 0)
161 {
162 gs_autoIdsLargeRefCount->erase (winid);
163 gs_autoIdsRefCount[winid] = ID_FREE;
164
165 if (gs_autoIdsLargeRefCount->empty())
166 wxDELETE (gs_autoIdsLargeRefCount);
167 }
168 }
cf2810aa 169 else
e3b2f973 170 gs_autoIdsRefCount[winid]--;
cf2810aa
VZ
171
172 wxLogTrace(wxTRACE_WINDOWID, wxT("Decreasing ref count of ID %d to %d"),
535a0e08 173 winid + wxID_AUTO_LOWEST, GetIdRefCount(winid + wxID_AUTO_LOWEST));
cf2810aa
VZ
174}
175
176#else // wxUSE_AUTOID_MANAGEMENT
177
178static wxWindowID gs_nextAutoId = wxID_AUTO_HIGHEST;
179
180#endif
181
182} // anonymous namespace
183
184
185#if wxUSE_AUTOID_MANAGEMENT
186
e3b2f973 187void wxWindowIDRef::Assign(wxWindowID winid)
cf2810aa 188{
e3b2f973 189 if ( winid != m_id )
cf2810aa
VZ
190 {
191 // decrease count if it is in the managed range
192 if ( m_id >= wxID_AUTO_LOWEST && m_id <= wxID_AUTO_HIGHEST )
193 DecIdRefCount(m_id);
194
e3b2f973 195 m_id = winid;
cf2810aa
VZ
196
197 // increase count if it is in the managed range
198 if ( m_id >= wxID_AUTO_LOWEST && m_id <= wxID_AUTO_HIGHEST )
199 IncIdRefCount(m_id);
200 }
201}
202
203#endif // wxUSE_AUTOID_MANAGEMENT
204
205
206
207wxWindowID wxIdManager::ReserveId(int count)
208{
209 wxASSERT_MSG(count > 0, wxT("can't allocate less than 1 id"));
210
211
212#if wxUSE_AUTOID_MANAGEMENT
213 if ( gs_nextAutoId + count - 1 <= wxID_AUTO_HIGHEST )
214 {
e3b2f973 215 wxWindowID winid = gs_nextAutoId;
cf2810aa
VZ
216
217 while(count--)
218 {
219 ReserveIdRefCount(gs_nextAutoId++);
220 }
221
e3b2f973 222 return winid;
cf2810aa
VZ
223 }
224 else
225 {
226 int found = 0;
227
e3b2f973 228 for(wxWindowID winid = wxID_AUTO_LOWEST; winid <= wxID_AUTO_HIGHEST; winid++)
cf2810aa 229 {
e3b2f973 230 if(GetIdRefCount(winid) == 0)
cf2810aa
VZ
231 {
232 found++;
233 if(found == count)
234 {
235 // Imagine this: 100 free IDs left. Then NewId(50) takes 50
236 // so 50 left. Then, the 25 before that last 50 are freed, but
237 // gs_nextAutoId does not decrement and stays where it is at
238 // with 50 free. Then NewId(75) gets called, and since there
239 // are only 50 left according to gs_nextAutoId, it does a
240 // search and finds the 75 at the end. Then NewId(10) gets
241 // called, and accorind to gs_nextAutoId, their are still
242 // 50 at the end so it returns them without testing the ref
243 // To fix this, the next ID is also updated here as needed
e3b2f973
SC
244 if(winid >= gs_nextAutoId)
245 gs_nextAutoId = winid + 1;
cf2810aa
VZ
246
247 while(count--)
e3b2f973 248 ReserveIdRefCount(winid--);
cf2810aa 249
e3b2f973 250 return winid + 1;
cf2810aa
VZ
251 }
252 }
253 else
254 {
255 found = 0;
256 }
257 }
258 }
259
34a083c9 260 wxLogError(_("Out of window IDs. Recommend shutting down application."));
cf2810aa
VZ
261 return wxID_NONE;
262#else // !wxUSE_AUTOID_MANAGEMENT
263 // Make sure enough in the range
e3b2f973 264 wxWindowID winid;
cf2810aa 265
e3b2f973 266 winid = gs_nextAutoId - count + 1;
cf2810aa 267
e3b2f973 268 if ( winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST )
cf2810aa
VZ
269 {
270 // There is enough, but it may be time to wrap
e3b2f973 271 if(winid == wxID_AUTO_LOWEST)
cf2810aa
VZ
272 gs_nextAutoId = wxID_AUTO_HIGHEST;
273 else
e3b2f973 274 gs_nextAutoId = winid - 1;
cf2810aa 275
e3b2f973 276 return winid;
cf2810aa
VZ
277 }
278 else
279 {
280 // There is not enough at the low end of the range or
281 // count was big enough to wrap around to the positive
282 // Surely 'count' is not so big to take up much of the range
283 gs_nextAutoId = wxID_AUTO_HIGHEST - count;
284 return gs_nextAutoId + 1;
285 }
286#endif // wxUSE_AUTOID_MANAGEMENT/!wxUSE_AUTOID_MANAGEMENT
287}
288
e3b2f973 289void wxIdManager::UnreserveId(wxWindowID winid, int count)
cf2810aa
VZ
290{
291 wxASSERT_MSG(count > 0, wxT("can't unreserve less than 1 id"));
292
293#if wxUSE_AUTOID_MANAGEMENT
294 while (count--)
e3b2f973 295 UnreserveIdRefCount(winid++);
cf2810aa 296#else
e3b2f973 297 wxUnusedVar(winid);
cf2810aa
VZ
298 wxUnusedVar(count);
299#endif
300}
301