2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51
/// Fast, efficient map for
52
/// <see cref="ObjectId">ObjectId</see>
53
/// subclasses in only one map.
55
/// To use this map type, applications must have their entry value type extend
57
/// <see cref="Entry">Entry</see>
58
/// , which itself extends from ObjectId.
60
/// Object instances may only be stored in <b>ONE</b> ObjectIdOwnerMap. This
61
/// restriction exists because the map stores internal map state within each
62
/// object instance. If an instance is be placed in another ObjectIdOwnerMap it
63
/// could corrupt one or both map's internal state.
65
/// If an object instance must be in more than one map, applications may use
66
/// ObjectIdOwnerMap for one of the maps, and
67
/// <see cref="ObjectIdSubclassMap{V}">ObjectIdSubclassMap<V></see>
69
/// other map(s). It is encouraged to use ObjectIdOwnerMap for the map that is
70
/// accessed most often, as this implementation runs faster than the more general
71
/// ObjectIdSubclassMap implementation.
74
public class ObjectIdOwnerMap<V> : Iterable<V> where V:ObjectIdOwnerMap.Entry
76
/// <summary>Size of the initial directory, will grow as necessary.</summary>
77
/// <remarks>Size of the initial directory, will grow as necessary.</remarks>
78
private const int INITIAL_DIRECTORY = 1024;
80
/// <summary>Number of bits in a segment's index.</summary>
81
/// <remarks>Number of bits in a segment's index. Segments are 2^11 in size.</remarks>
82
private const int SEGMENT_BITS = 11;
84
private const int SEGMENT_SHIFT = 32 - SEGMENT_BITS;
86
/// <summary>Top level directory of the segments.</summary>
88
/// Top level directory of the segments.
91
/// <see cref="ObjectIdOwnerMap{V}.bits">ObjectIdOwnerMap<V>.bits</see>
92
/// of the SHA-1 are used to select the segment from
93
/// this directory. Each segment is constant sized at 2^SEGMENT_BITS.
95
private V[][] directory;
97
/// <summary>Total number of objects in this map.</summary>
98
/// <remarks>Total number of objects in this map.</remarks>
102
/// The map doubles in capacity when
103
/// <see cref="ObjectIdOwnerMap{V}.size">ObjectIdOwnerMap<V>.size</see>
104
/// reaches this target.
109
/// Number of low bits used to form the index into
110
/// <see cref="ObjectIdOwnerMap{V}.directory">ObjectIdOwnerMap<V>.directory</see>
116
/// Low bit mask to index into
117
/// <see cref="ObjectIdOwnerMap{V}.directory">ObjectIdOwnerMap<V>.directory</see>
119
/// <code>2^bits-1</code>
124
/// <summary>Create an empty map.</summary>
125
/// <remarks>Create an empty map.</remarks>
126
public ObjectIdOwnerMap()
130
grow = ComputeGrowAt(bits);
131
directory = new V[INITIAL_DIRECTORY][];
132
directory[0] = NewSegment();
135
/// <summary>Remove all entries from this map.</summary>
136
/// <remarks>Remove all entries from this map.</remarks>
137
public virtual void Clear()
140
foreach (V[] tbl in directory)
146
Arrays.Fill(tbl, null);
150
/// <summary>Lookup an existing mapping.</summary>
151
/// <remarks>Lookup an existing mapping.</remarks>
152
/// <param name="toFind">the object identifier to find.</param>
153
/// <returns>the instance mapped to toFind, or null if no mapping exists.</returns>
154
public virtual V Get(AnyObjectId toFind)
157
V obj = directory[h & mask][(int)(((uint)h) >> SEGMENT_SHIFT)];
158
for (; obj != null; obj = (V)obj.next)
160
if (Equals(obj, toFind))
168
/// <summary>Returns true if this map contains the specified object.</summary>
169
/// <remarks>Returns true if this map contains the specified object.</remarks>
170
/// <param name="toFind">object to find.</param>
171
/// <returns>true if the mapping exists for this object; false otherwise.</returns>
172
public virtual bool Contains(AnyObjectId toFind)
174
return Get(toFind) != null;
177
/// <summary>Store an object for future lookup.</summary>
179
/// Store an object for future lookup.
181
/// An existing mapping for <b>must not</b> be in this map. Callers must
183
/// <see cref="ObjectIdOwnerMap{V}.Get(AnyObjectId)">ObjectIdOwnerMap<V>.Get(AnyObjectId)
185
/// to verify there is no current
186
/// mapping prior to adding a new mapping, or use
187
/// <see cref="ObjectIdOwnerMap{V}.AddIfAbsent{Q}(Entry)">ObjectIdOwnerMap<V>.AddIfAbsent<Q>(Entry)
191
/// <param name="newValue">the object to store.</param>
193
public virtual void Add<Q>(Q newValue) where Q:V
199
int h = ((V)newValue).w1;
200
V[] table = directory[h & mask];
201
h = (int)(((uint)h) >> SEGMENT_SHIFT);
202
newValue.next = table[h];
206
/// <summary>Store an object for future lookup.</summary>
208
/// Store an object for future lookup.
211
/// <code>newValue</code>
212
/// , but only if there is not already an object for
213
/// the same object name. Callers can tell if the value is new by checking
214
/// the return value with reference equality:
217
/// boolean wasNew = map.addIfAbsent(obj) == obj;
220
/// <param name="newValue">the object to store.</param>
223
/// <code>newValue</code>
224
/// if stored, or the prior value already stored and
225
/// that would have been returned had the caller used
226
/// <code>get(newValue)</code>
230
public virtual V AddIfAbsent<Q>(Q newValue) where Q:V
232
int h = ((V)newValue).w1;
233
V[] table = directory[h & mask];
234
h = (int)(((uint)h) >> SEGMENT_SHIFT);
235
for (V obj = table[h]; obj != null; obj = (V)obj.next)
237
if (Equals(obj, newValue))
242
newValue.next = table[h];
251
/// <returns>number of objects in this map.</returns>
252
public virtual int Size()
259
/// <see cref="ObjectIdOwnerMap{V}.Size()">ObjectIdOwnerMap<V>.Size()</see>
262
public virtual bool IsEmpty()
267
public override Sharpen.Iterator<V> Iterator()
269
return new _Iterator_223(this);
272
private sealed class _Iterator_223 : Sharpen.Iterator<V>
274
public _Iterator_223(ObjectIdOwnerMap<V> _enclosing)
276
this._enclosing = _enclosing;
287
public override bool HasNext()
289
return this.found < this._enclosing.size;
292
public override V Next()
294
if (this.nextv != null)
296
return this.Foundv(this.nextv);
300
V[] table = this._enclosing.directory[this.dirIdx];
301
if (this.tblIdx == table.Length)
303
if (++this.dirIdx >= (1 << this._enclosing.bits))
305
throw new NoSuchElementException();
307
table = this._enclosing.directory[this.dirIdx];
310
while (this.tblIdx < table.Length)
312
V v = table[this.tblIdx++];
315
return this.Foundv(v);
321
private V Foundv(V v)
324
this.nextv = (V)v.next;
328
public override void Remove()
330
throw new NotSupportedException();
333
private readonly ObjectIdOwnerMap<V> _enclosing;
338
int oldDirLen = 1 << bits;
341
mask = (1 << bits) - 1;
342
grow = ComputeGrowAt(bits);
343
// Quadruple the directory if it needs to expand. Expanding the
344
// directory is expensive because it generates garbage, so try
345
// to avoid doing it often.
347
int newDirLen = 1 << bits;
348
if (directory.Length < newDirLen)
350
V[][] newDir = (V[][])new ObjectIdOwnerMap.Entry[newDirLen << 1][];
351
System.Array.Copy(directory, 0, newDir, 0, oldDirLen);
354
// For every bucket of every old segment, split the chain between
355
// the old segment and the new segment's corresponding bucket. To
356
// select between them use the lowest bit that was just added into
357
// the mask above. This causes the table to double in capacity.
359
for (int dirIdx = 0; dirIdx < oldDirLen; dirIdx++)
361
V[] oldTable = directory[dirIdx];
362
V[] newTable = NewSegment();
363
for (int i = 0; i < oldTable.Length; i++)
368
for (V obj = oldTable[i]; obj != null; obj = next)
371
if ((obj.w1 & s) == 0)
382
oldTable[i] = chain0;
383
newTable[i] = chain1;
385
directory[oldDirLen + dirIdx] = newTable;
389
private V[] NewSegment()
391
return new V[1 << SEGMENT_BITS];
394
private static int ComputeGrowAt(int bits)
396
return 1 << (bits + SEGMENT_BITS);
399
private static bool Equals(AnyObjectId firstObjectId, AnyObjectId secondObjectId)
401
return firstObjectId.w2 == secondObjectId.w2 && firstObjectId.w3 == secondObjectId
402
.w3 && firstObjectId.w4 == secondObjectId.w4 && firstObjectId.w5 == secondObjectId
403
.w5 && firstObjectId.w1 == secondObjectId.w1;
407
public class ObjectIdOwnerMap
410
/// Type of entry stored in the
411
/// <see cref="ObjectIdOwnerMap{V}">ObjectIdOwnerMap<V></see>
414
[System.Serializable]
415
public abstract class Entry : ObjectId
417
internal ObjectIdOwnerMap.Entry next;
419
/// <summary>Initialize this entry with a specific ObjectId.</summary>
420
/// <remarks>Initialize this entry with a specific ObjectId.</remarks>
421
/// <param name="id">the id the entry represents.</param>
422
protected internal Entry(AnyObjectId id) : base(id)