-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathHandleVector.cs
More file actions
232 lines (201 loc) · 6.56 KB
/
HandleVector.cs
File metadata and controls
232 lines (201 loc) · 6.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
using System;
using System.Collections.Generic;
using System.Diagnostics;
/*
* HandleVector<T>
*
* Description:
* -------------
* HandleVector<T> is a dynamic container designed to efficiently manage objects
* using handles instead of direct references. It supports adding, retrieving,
* and releasing objects dynamically while ensuring safety through handle validation.
*
* This structure is particularly useful for scenarios where objects need to
* be frequently created and destroyed, such as entity management in game engines,
* resource pooling, or managing dynamic assets.
*
* Features:
* ---------
* 1. Dynamic Allocation: Objects can be added dynamically, and their handles can be used
* to reference them.
* 2. Handle-Based Access: Provides a type-safe mechanism to access objects using
* unique handles, preventing direct reference misuse.
* 3. Efficient Reuse: Released handles are recycled to minimize memory overhead
* and optimize performance.
* 4. Version Control: Each handle includes a version number to detect stale or
* invalid handles.
*
* Example Usage:
* --------------
* 1. Add an object to the container:
* var handle = handleVector.Add(myObject);
* 2. Retrieve the object using the handle:
* var retrievedObject = handleVector.GetItem(handle);
* 3. Release the object when it is no longer needed:
* handleVector.Release(handle);
* 4. Check the validity of a handle:
* bool isValid = handleVector.IsValid(handle);
*/
namespace ZeroPass
{
public class HandleVector<T>
{
[DebuggerDisplay("{index}")]
public struct Handle : IComparable<Handle>, IEquatable<Handle>
{
private const int InvalidIndex = 0;
private int _index;
public static readonly Handle InvalidHandle = new Handle
{
_index = 0
};
public int index
{
get
{
return _index - 1;
}
set
{
_index = value + 1;
}
}
public bool IsValid()
{
return _index != 0;
}
public void Clear()
{
_index = 0;
}
public int CompareTo(Handle obj)
{
return _index - obj._index;
}
public override bool Equals(object obj)
{
Handle handle = (Handle)obj;
return _index == handle._index;
}
public bool Equals(Handle other)
{
return _index == other._index;
}
public override int GetHashCode()
{
return _index;
}
public static bool operator ==(Handle x, Handle y)
{
return x._index == y._index;
}
public static bool operator !=(Handle x, Handle y)
{
return x._index != y._index;
}
}
public static readonly Handle InvalidHandle = Handle.InvalidHandle;
protected Stack<Handle> freeHandles;
protected List<T> items;
protected List<byte> versions;
public List<T> Items => items;
public Stack<Handle> Handles => freeHandles;
public HandleVector(int initial_size)
{
freeHandles = new Stack<Handle>(initial_size);
items = new List<T>(initial_size);
versions = new List<byte>(initial_size);
Initialize(initial_size);
}
public virtual void Clear()
{
items.Clear();
freeHandles.Clear();
versions.Clear();
}
private void Initialize(int size)
{
for (int num = size - 1; num >= 0; num--)
{
freeHandles.Push(new Handle
{
index = num
});
items.Add(default(T));
versions.Add(0);
}
}
public virtual Handle Add(T item)
{
Handle handle;
if (freeHandles.Count > 0)
{
handle = freeHandles.Pop();
UnpackHandle(handle, out byte _, out int index);
items[index] = item;
}
else
{
versions.Add(0);
handle = PackHandle(items.Count);
items.Add(item);
}
return handle;
}
public virtual T Release(Handle handle)
{
if (!handle.IsValid())
{
return default(T);
}
UnpackHandle(handle, out byte version, out int index);
version = (byte)(version + 1);
versions[index] = version;
Debug.Assert(index >= 0);
Debug.Assert(index < 16777216);
handle = PackHandle(index);
freeHandles.Push(handle);
T result = items[index];
items[index] = default(T);
return result;
}
public T GetItem(Handle handle)
{
UnpackHandle(handle, out byte _, out int index);
return items[index];
}
private Handle PackHandle(int index)
{
Debug.Assert(index < 16777216);
byte b = versions[index];
versions[index] = b;
Handle invalidHandle = InvalidHandle;
invalidHandle.index = ((b << 24) | index);
return invalidHandle;
}
public void UnpackHandle(Handle handle, out byte version, out int index)
{
version = (byte)(handle.index >> 24);
index = (handle.index & 0xFFFFFF);
if (versions[index] != version)
{
throw new ArgumentException("Accessing mismatched handle version. Expected version=" + versions[index].ToString() + " but got version=" + version.ToString());
}
}
public void UnpackHandleUnchecked(Handle handle, out byte version, out int index)
{
version = (byte)(handle.index >> 24);
index = (handle.index & 0xFFFFFF);
}
public bool IsValid(Handle handle)
{
return (handle.index & 0xFFFFFF) != 16777215;
}
public bool IsVersionValid(Handle handle)
{
byte b = (byte)(handle.index >> 24);
int index = handle.index & 0xFFFFFF;
return b == versions[index];
}
}
}