~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit.Dircache/DirCacheTree.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
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
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
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.
 
23
 
 
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
 
27
  written permission.
 
28
 
 
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.
 
42
*/
 
43
 
 
44
using System.Collections.Generic;
 
45
using System.Text;
 
46
using NGit;
 
47
using NGit.Dircache;
 
48
using NGit.Errors;
 
49
using NGit.Util;
 
50
using Sharpen;
 
51
 
 
52
namespace NGit.Dircache
 
53
{
 
54
        /// <summary>
 
55
        /// Single tree record from the 'TREE'
 
56
        /// <see cref="DirCache">DirCache</see>
 
57
        /// extension.
 
58
        /// <p>
 
59
        /// A valid cache tree record contains the object id of a tree object and the
 
60
        /// total number of
 
61
        /// <see cref="DirCacheEntry">DirCacheEntry</see>
 
62
        /// instances (counted recursively) from
 
63
        /// the DirCache contained within the tree. This information facilitates faster
 
64
        /// traversal of the index and quicker generation of tree objects prior to
 
65
        /// creating a new commit.
 
66
        /// <p>
 
67
        /// An invalid cache tree record indicates a known subtree whose file entries
 
68
        /// have changed in ways that cause the tree to no longer have a known object id.
 
69
        /// Invalid cache tree records must be revalidated prior to use.
 
70
        /// </summary>
 
71
        public class DirCacheTree
 
72
        {
 
73
                private static readonly byte[] NO_NAME = new byte[] {  };
 
74
 
 
75
                private static readonly NGit.Dircache.DirCacheTree[] NO_CHILDREN = new NGit.Dircache.DirCacheTree
 
76
                        [] {  };
 
77
 
 
78
                private sealed class _IComparer_82 : IComparer<NGit.Dircache.DirCacheTree>
 
79
                {
 
80
                        public _IComparer_82()
 
81
                        {
 
82
                        }
 
83
 
 
84
                        public int Compare(NGit.Dircache.DirCacheTree o1, NGit.Dircache.DirCacheTree o2)
 
85
                        {
 
86
                                byte[] a = o1.encodedName;
 
87
                                byte[] b = o2.encodedName;
 
88
                                int aLen = a.Length;
 
89
                                int bLen = b.Length;
 
90
                                int cPos;
 
91
                                for (cPos = 0; cPos < aLen && cPos < bLen; cPos++)
 
92
                                {
 
93
                                        int cmp = (a[cPos] & unchecked((int)(0xff))) - (b[cPos] & unchecked((int)(0xff)));
 
94
                                        if (cmp != 0)
 
95
                                        {
 
96
                                                return cmp;
 
97
                                        }
 
98
                                }
 
99
                                if (aLen == bLen)
 
100
                                {
 
101
                                        return 0;
 
102
                                }
 
103
                                if (aLen < bLen)
 
104
                                {
 
105
                                        return '/' - (b[cPos] & unchecked((int)(0xff)));
 
106
                                }
 
107
                                return (a[cPos] & unchecked((int)(0xff))) - '/';
 
108
                        }
 
109
                }
 
110
 
 
111
                private static readonly IComparer<NGit.Dircache.DirCacheTree> TREE_CMP = new _IComparer_82
 
112
                        ();
 
113
 
 
114
                /// <summary>Tree this tree resides in; null if we are the root.</summary>
 
115
                /// <remarks>Tree this tree resides in; null if we are the root.</remarks>
 
116
                private NGit.Dircache.DirCacheTree parent;
 
117
 
 
118
                /// <summary>Name of this tree within its parent.</summary>
 
119
                /// <remarks>Name of this tree within its parent.</remarks>
 
120
                private byte[] encodedName;
 
121
 
 
122
                /// <summary>
 
123
                /// Number of
 
124
                /// <see cref="DirCacheEntry">DirCacheEntry</see>
 
125
                /// records that belong to this tree.
 
126
                /// </summary>
 
127
                private int entrySpan;
 
128
 
 
129
                /// <summary>Unique SHA-1 of this tree; null if invalid.</summary>
 
130
                /// <remarks>Unique SHA-1 of this tree; null if invalid.</remarks>
 
131
                private ObjectId id;
 
132
 
 
133
                /// <summary>
 
134
                /// Child trees, if any, sorted by
 
135
                /// <see cref="encodedName">encodedName</see>
 
136
                /// .
 
137
                /// </summary>
 
138
                private NGit.Dircache.DirCacheTree[] children;
 
139
 
 
140
                /// <summary>
 
141
                /// Number of valid children in
 
142
                /// <see cref="children">children</see>
 
143
                /// .
 
144
                /// </summary>
 
145
                private int childCnt;
 
146
 
 
147
                public DirCacheTree()
 
148
                {
 
149
                        encodedName = NO_NAME;
 
150
                        children = NO_CHILDREN;
 
151
                        childCnt = 0;
 
152
                        entrySpan = -1;
 
153
                }
 
154
 
 
155
                private DirCacheTree(NGit.Dircache.DirCacheTree myParent, byte[] path, int pathOff
 
156
                        , int pathLen)
 
157
                {
 
158
                        parent = myParent;
 
159
                        encodedName = new byte[pathLen];
 
160
                        System.Array.Copy(path, pathOff, encodedName, 0, pathLen);
 
161
                        children = NO_CHILDREN;
 
162
                        childCnt = 0;
 
163
                        entrySpan = -1;
 
164
                }
 
165
 
 
166
                internal DirCacheTree(byte[] @in, MutableInteger off, NGit.Dircache.DirCacheTree 
 
167
                        myParent)
 
168
                {
 
169
                        parent = myParent;
 
170
                        int ptr = RawParseUtils.Next(@in, off.value, '\0');
 
171
                        int nameLen = ptr - off.value - 1;
 
172
                        if (nameLen > 0)
 
173
                        {
 
174
                                encodedName = new byte[nameLen];
 
175
                                System.Array.Copy(@in, off.value, encodedName, 0, nameLen);
 
176
                        }
 
177
                        else
 
178
                        {
 
179
                                encodedName = NO_NAME;
 
180
                        }
 
181
                        entrySpan = RawParseUtils.ParseBase10(@in, ptr, off);
 
182
                        int subcnt = RawParseUtils.ParseBase10(@in, off.value, off);
 
183
                        off.value = RawParseUtils.Next(@in, off.value, '\n');
 
184
                        if (entrySpan >= 0)
 
185
                        {
 
186
                                // Valid trees have a positive entry count and an id of a
 
187
                                // tree object that should exist in the object database.
 
188
                                //
 
189
                                id = ObjectId.FromRaw(@in, off.value);
 
190
                                off.value += Constants.OBJECT_ID_LENGTH;
 
191
                        }
 
192
                        if (subcnt > 0)
 
193
                        {
 
194
                                bool alreadySorted = true;
 
195
                                children = new NGit.Dircache.DirCacheTree[subcnt];
 
196
                                for (int i = 0; i < subcnt; i++)
 
197
                                {
 
198
                                        children[i] = new NGit.Dircache.DirCacheTree(@in, off, this);
 
199
                                        // C Git's ordering differs from our own; it prefers to
 
200
                                        // sort by length first. This sometimes produces a sort
 
201
                                        // we do not desire. On the other hand it may have been
 
202
                                        // created by us, and be sorted the way we want.
 
203
                                        //
 
204
                                        if (alreadySorted && i > 0 && TREE_CMP.Compare(children[i - 1], children[i]) > 0)
 
205
                                        {
 
206
                                                alreadySorted = false;
 
207
                                        }
 
208
                                }
 
209
                                if (!alreadySorted)
 
210
                                {
 
211
                                        Arrays.Sort(children, 0, subcnt, TREE_CMP);
 
212
                                }
 
213
                        }
 
214
                        else
 
215
                        {
 
216
                                // Leaf level trees have no children, only (file) entries.
 
217
                                //
 
218
                                children = NO_CHILDREN;
 
219
                        }
 
220
                        childCnt = subcnt;
 
221
                }
 
222
 
 
223
                /// <exception cref="System.IO.IOException"></exception>
 
224
                internal virtual void Write(byte[] tmp, OutputStream os)
 
225
                {
 
226
                        int ptr = tmp.Length;
 
227
                        tmp[--ptr] = (byte)('\n');
 
228
                        ptr = RawParseUtils.FormatBase10(tmp, ptr, childCnt);
 
229
                        tmp[--ptr] = (byte)(' ');
 
230
                        ptr = RawParseUtils.FormatBase10(tmp, ptr, IsValid() ? entrySpan : -1);
 
231
                        tmp[--ptr] = 0;
 
232
                        os.Write(encodedName);
 
233
                        os.Write(tmp, ptr, tmp.Length - ptr);
 
234
                        if (IsValid())
 
235
                        {
 
236
                                id.CopyRawTo(tmp, 0);
 
237
                                os.Write(tmp, 0, Constants.OBJECT_ID_LENGTH);
 
238
                        }
 
239
                        for (int i = 0; i < childCnt; i++)
 
240
                        {
 
241
                                children[i].Write(tmp, os);
 
242
                        }
 
243
                }
 
244
 
 
245
                /// <summary>Determine if this cache is currently valid.</summary>
 
246
                /// <remarks>
 
247
                /// Determine if this cache is currently valid.
 
248
                /// <p>
 
249
                /// A valid cache tree knows how many
 
250
                /// <see cref="DirCacheEntry">DirCacheEntry</see>
 
251
                /// instances from
 
252
                /// the parent
 
253
                /// <see cref="DirCache">DirCache</see>
 
254
                /// reside within this tree (recursively
 
255
                /// enumerated). It also knows the object id of the tree, as the tree should
 
256
                /// be readily available from the repository's object database.
 
257
                /// </remarks>
 
258
                /// <returns>
 
259
                /// true if this tree is knows key details about itself; false if the
 
260
                /// tree needs to be regenerated.
 
261
                /// </returns>
 
262
                public virtual bool IsValid()
 
263
                {
 
264
                        return id != null;
 
265
                }
 
266
 
 
267
                /// <summary>Get the number of entries this tree spans within the DirCache.</summary>
 
268
                /// <remarks>
 
269
                /// Get the number of entries this tree spans within the DirCache.
 
270
                /// <p>
 
271
                /// If this tree is not valid (see
 
272
                /// <see cref="IsValid()">IsValid()</see>
 
273
                /// ) this method's return
 
274
                /// value is always strictly negative (less than 0) but is otherwise an
 
275
                /// undefined result.
 
276
                /// </remarks>
 
277
                /// <returns>total number of entries (recursively) contained within this tree.</returns>
 
278
                public virtual int GetEntrySpan()
 
279
                {
 
280
                        return entrySpan;
 
281
                }
 
282
 
 
283
                /// <summary>Get the number of cached subtrees contained within this tree.</summary>
 
284
                /// <remarks>Get the number of cached subtrees contained within this tree.</remarks>
 
285
                /// <returns>number of child trees available through this tree.</returns>
 
286
                public virtual int GetChildCount()
 
287
                {
 
288
                        return childCnt;
 
289
                }
 
290
 
 
291
                /// <summary>Get the i-th child cache tree.</summary>
 
292
                /// <remarks>Get the i-th child cache tree.</remarks>
 
293
                /// <param name="i">index of the child to obtain.</param>
 
294
                /// <returns>the child tree.</returns>
 
295
                public virtual NGit.Dircache.DirCacheTree GetChild(int i)
 
296
                {
 
297
                        return children[i];
 
298
                }
 
299
 
 
300
                internal virtual ObjectId GetObjectId()
 
301
                {
 
302
                        return id;
 
303
                }
 
304
 
 
305
                /// <summary>Get the tree's name within its parent.</summary>
 
306
                /// <remarks>
 
307
                /// Get the tree's name within its parent.
 
308
                /// <p>
 
309
                /// This method is not very efficient and is primarily meant for debugging
 
310
                /// and final output generation. Applications should try to avoid calling it,
 
311
                /// and if invoked do so only once per interesting entry, where the name is
 
312
                /// absolutely required for correct function.
 
313
                /// </remarks>
 
314
                /// <returns>name of the tree. This does not contain any '/' characters.</returns>
 
315
                public virtual string GetNameString()
 
316
                {
 
317
                        ByteBuffer bb = ByteBuffer.Wrap(encodedName);
 
318
                        return Constants.CHARSET.Decode(bb).ToString();
 
319
                }
 
320
 
 
321
                /// <summary>Get the tree's path within the repository.</summary>
 
322
                /// <remarks>
 
323
                /// Get the tree's path within the repository.
 
324
                /// <p>
 
325
                /// This method is not very efficient and is primarily meant for debugging
 
326
                /// and final output generation. Applications should try to avoid calling it,
 
327
                /// and if invoked do so only once per interesting entry, where the name is
 
328
                /// absolutely required for correct function.
 
329
                /// </remarks>
 
330
                /// <returns>
 
331
                /// path of the tree, relative to the repository root. If this is not
 
332
                /// the root tree the path ends with '/'. The root tree's path string
 
333
                /// is the empty string ("").
 
334
                /// </returns>
 
335
                public virtual string GetPathString()
 
336
                {
 
337
                        StringBuilder r = new StringBuilder();
 
338
                        AppendName(r);
 
339
                        return r.ToString();
 
340
                }
 
341
 
 
342
                /// <summary>Write (if necessary) this tree to the object store.</summary>
 
343
                /// <remarks>Write (if necessary) this tree to the object store.</remarks>
 
344
                /// <param name="cache">the complete cache from DirCache.</param>
 
345
                /// <param name="cIdx">
 
346
                /// first position of <code>cache</code> that is a member of this
 
347
                /// tree. The path of <code>cache[cacheIdx].path</code> for the
 
348
                /// range <code>[0,pathOff-1)</code> matches the complete path of
 
349
                /// this tree, from the root of the repository.
 
350
                /// </param>
 
351
                /// <param name="pathOffset">
 
352
                /// number of bytes of <code>cache[cacheIdx].path</code> that
 
353
                /// matches this tree's path. The value at array position
 
354
                /// <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if
 
355
                /// <code>pathOff</code> is &gt; 0.
 
356
                /// </param>
 
357
                /// <param name="ow">the writer to use when serializing to the store.</param>
 
358
                /// <returns>identity of this tree.</returns>
 
359
                /// <exception cref="NGit.Errors.UnmergedPathException">
 
360
                /// one or more paths contain higher-order stages (stage &gt; 0),
 
361
                /// which cannot be stored in a tree object.
 
362
                /// </exception>
 
363
                /// <exception cref="System.IO.IOException">an unexpected error occurred writing to the object store.
 
364
                ///     </exception>
 
365
                internal virtual ObjectId WriteTree(DirCacheEntry[] cache, int cIdx, int pathOffset
 
366
                        , ObjectInserter ow)
 
367
                {
 
368
                        if (id == null)
 
369
                        {
 
370
                                int endIdx = cIdx + entrySpan;
 
371
                                TreeFormatter fmt = new TreeFormatter(ComputeSize(cache, cIdx, pathOffset, ow));
 
372
                                int childIdx = 0;
 
373
                                int entryIdx = cIdx;
 
374
                                while (entryIdx < endIdx)
 
375
                                {
 
376
                                        DirCacheEntry e = cache[entryIdx];
 
377
                                        byte[] ep = e.path;
 
378
                                        if (childIdx < childCnt)
 
379
                                        {
 
380
                                                NGit.Dircache.DirCacheTree st = children[childIdx];
 
381
                                                if (st.Contains(ep, pathOffset, ep.Length))
 
382
                                                {
 
383
                                                        fmt.Append(st.encodedName, FileMode.TREE, st.id);
 
384
                                                        entryIdx += st.entrySpan;
 
385
                                                        childIdx++;
 
386
                                                        continue;
 
387
                                                }
 
388
                                        }
 
389
                                        fmt.Append(ep, pathOffset, ep.Length - pathOffset, e.FileMode, e.IdBuffer, e.IdOffset
 
390
                                                );
 
391
                                        entryIdx++;
 
392
                                }
 
393
                                id = ow.Insert(fmt);
 
394
                        }
 
395
                        return id;
 
396
                }
 
397
 
 
398
                /// <exception cref="NGit.Errors.UnmergedPathException"></exception>
 
399
                /// <exception cref="System.IO.IOException"></exception>
 
400
                private int ComputeSize(DirCacheEntry[] cache, int cIdx, int pathOffset, ObjectInserter
 
401
                         ow)
 
402
                {
 
403
                        int endIdx = cIdx + entrySpan;
 
404
                        int childIdx = 0;
 
405
                        int entryIdx = cIdx;
 
406
                        int size = 0;
 
407
                        while (entryIdx < endIdx)
 
408
                        {
 
409
                                DirCacheEntry e = cache[entryIdx];
 
410
                                if (e.Stage != 0)
 
411
                                {
 
412
                                        throw new UnmergedPathException(e);
 
413
                                }
 
414
                                byte[] ep = e.path;
 
415
                                if (childIdx < childCnt)
 
416
                                {
 
417
                                        NGit.Dircache.DirCacheTree st = children[childIdx];
 
418
                                        if (st.Contains(ep, pathOffset, ep.Length))
 
419
                                        {
 
420
                                                int stOffset = pathOffset + st.NameLength() + 1;
 
421
                                                st.WriteTree(cache, entryIdx, stOffset, ow);
 
422
                                                size += TreeFormatter.EntrySize(FileMode.TREE, st.NameLength());
 
423
                                                entryIdx += st.entrySpan;
 
424
                                                childIdx++;
 
425
                                                continue;
 
426
                                        }
 
427
                                }
 
428
                                size += TreeFormatter.EntrySize(e.FileMode, ep.Length - pathOffset);
 
429
                                entryIdx++;
 
430
                        }
 
431
                        return size;
 
432
                }
 
433
 
 
434
                private void AppendName(StringBuilder r)
 
435
                {
 
436
                        if (parent != null)
 
437
                        {
 
438
                                parent.AppendName(r);
 
439
                                r.Append(GetNameString());
 
440
                                r.Append('/');
 
441
                        }
 
442
                        else
 
443
                        {
 
444
                                if (NameLength() > 0)
 
445
                                {
 
446
                                        r.Append(GetNameString());
 
447
                                        r.Append('/');
 
448
                                }
 
449
                        }
 
450
                }
 
451
 
 
452
                internal int NameLength()
 
453
                {
 
454
                        return encodedName.Length;
 
455
                }
 
456
 
 
457
                internal bool Contains(byte[] a, int aOff, int aLen)
 
458
                {
 
459
                        byte[] e = encodedName;
 
460
                        int eLen = e.Length;
 
461
                        for (int eOff = 0; eOff < eLen && aOff < aLen; eOff++, aOff++)
 
462
                        {
 
463
                                if (e[eOff] != a[aOff])
 
464
                                {
 
465
                                        return false;
 
466
                                }
 
467
                        }
 
468
                        if (aOff == aLen)
 
469
                        {
 
470
                                return false;
 
471
                        }
 
472
                        return a[aOff] == '/';
 
473
                }
 
474
 
 
475
                /// <summary>Update (if necessary) this tree's entrySpan.</summary>
 
476
                /// <remarks>Update (if necessary) this tree's entrySpan.</remarks>
 
477
                /// <param name="cache">the complete cache from DirCache.</param>
 
478
                /// <param name="cCnt">
 
479
                /// number of entries in <code>cache</code> that are valid for
 
480
                /// iteration.
 
481
                /// </param>
 
482
                /// <param name="cIdx">
 
483
                /// first position of <code>cache</code> that is a member of this
 
484
                /// tree. The path of <code>cache[cacheIdx].path</code> for the
 
485
                /// range <code>[0,pathOff-1)</code> matches the complete path of
 
486
                /// this tree, from the root of the repository.
 
487
                /// </param>
 
488
                /// <param name="pathOff">
 
489
                /// number of bytes of <code>cache[cacheIdx].path</code> that
 
490
                /// matches this tree's path. The value at array position
 
491
                /// <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if
 
492
                /// <code>pathOff</code> is &gt; 0.
 
493
                /// </param>
 
494
                internal virtual void Validate(DirCacheEntry[] cache, int cCnt, int cIdx, int pathOff
 
495
                        )
 
496
                {
 
497
                        if (entrySpan >= 0)
 
498
                        {
 
499
                                // If we are valid, our children are also valid.
 
500
                                // We have no need to validate them.
 
501
                                //
 
502
                                return;
 
503
                        }
 
504
                        entrySpan = 0;
 
505
                        if (cCnt == 0)
 
506
                        {
 
507
                                // Special case of an empty index, and we are the root tree.
 
508
                                //
 
509
                                return;
 
510
                        }
 
511
                        byte[] firstPath = cache[cIdx].path;
 
512
                        int stIdx = 0;
 
513
                        while (cIdx < cCnt)
 
514
                        {
 
515
                                byte[] currPath = cache[cIdx].path;
 
516
                                if (pathOff > 0 && !Peq(firstPath, currPath, pathOff))
 
517
                                {
 
518
                                        // The current entry is no longer in this tree. Our
 
519
                                        // span is updated and the remainder goes elsewhere.
 
520
                                        //
 
521
                                        break;
 
522
                                }
 
523
                                NGit.Dircache.DirCacheTree st = stIdx < childCnt ? children[stIdx] : null;
 
524
                                int cc = Namecmp(currPath, pathOff, st);
 
525
                                if (cc > 0)
 
526
                                {
 
527
                                        // This subtree is now empty.
 
528
                                        //
 
529
                                        RemoveChild(stIdx);
 
530
                                        continue;
 
531
                                }
 
532
                                if (cc < 0)
 
533
                                {
 
534
                                        int p = Slash(currPath, pathOff);
 
535
                                        if (p < 0)
 
536
                                        {
 
537
                                                // The entry has no '/' and thus is directly in this
 
538
                                                // tree. Count it as one of our own.
 
539
                                                //
 
540
                                                cIdx++;
 
541
                                                entrySpan++;
 
542
                                                continue;
 
543
                                        }
 
544
                                        // Build a new subtree for this entry.
 
545
                                        //
 
546
                                        st = new NGit.Dircache.DirCacheTree(this, currPath, pathOff, p - pathOff);
 
547
                                        InsertChild(stIdx, st);
 
548
                                }
 
549
                                // The entry is contained in this subtree.
 
550
                                //
 
551
                                st.Validate(cache, cCnt, cIdx, pathOff + st.NameLength() + 1);
 
552
                                cIdx += st.entrySpan;
 
553
                                entrySpan += st.entrySpan;
 
554
                                stIdx++;
 
555
                        }
 
556
                        // None of our remaining children can be in this tree
 
557
                        // as the current cache entry is after our own name.
 
558
                        //
 
559
                        while (stIdx < childCnt)
 
560
                        {
 
561
                                RemoveChild(childCnt - 1);
 
562
                        }
 
563
                }
 
564
 
 
565
                private void InsertChild(int stIdx, NGit.Dircache.DirCacheTree st)
 
566
                {
 
567
                        NGit.Dircache.DirCacheTree[] c = children;
 
568
                        if (childCnt + 1 <= c.Length)
 
569
                        {
 
570
                                if (stIdx < childCnt)
 
571
                                {
 
572
                                        System.Array.Copy(c, stIdx, c, stIdx + 1, childCnt - stIdx);
 
573
                                }
 
574
                                c[stIdx] = st;
 
575
                                childCnt++;
 
576
                                return;
 
577
                        }
 
578
                        int n = c.Length;
 
579
                        NGit.Dircache.DirCacheTree[] a = new NGit.Dircache.DirCacheTree[n + 1];
 
580
                        if (stIdx > 0)
 
581
                        {
 
582
                                System.Array.Copy(c, 0, a, 0, stIdx);
 
583
                        }
 
584
                        a[stIdx] = st;
 
585
                        if (stIdx < n)
 
586
                        {
 
587
                                System.Array.Copy(c, stIdx, a, stIdx + 1, n - stIdx);
 
588
                        }
 
589
                        children = a;
 
590
                        childCnt++;
 
591
                }
 
592
 
 
593
                private void RemoveChild(int stIdx)
 
594
                {
 
595
                        int n = --childCnt;
 
596
                        if (stIdx < n)
 
597
                        {
 
598
                                System.Array.Copy(children, stIdx + 1, children, stIdx, n - stIdx);
 
599
                        }
 
600
                        children[n] = null;
 
601
                }
 
602
 
 
603
                internal static bool Peq(byte[] a, byte[] b, int aLen)
 
604
                {
 
605
                        if (b.Length < aLen)
 
606
                        {
 
607
                                return false;
 
608
                        }
 
609
                        for (aLen--; aLen >= 0; aLen--)
 
610
                        {
 
611
                                if (a[aLen] != b[aLen])
 
612
                                {
 
613
                                        return false;
 
614
                                }
 
615
                        }
 
616
                        return true;
 
617
                }
 
618
 
 
619
                private static int Namecmp(byte[] a, int aPos, NGit.Dircache.DirCacheTree ct)
 
620
                {
 
621
                        if (ct == null)
 
622
                        {
 
623
                                return -1;
 
624
                        }
 
625
                        byte[] b = ct.encodedName;
 
626
                        int aLen = a.Length;
 
627
                        int bLen = b.Length;
 
628
                        int bPos = 0;
 
629
                        for (; aPos < aLen && bPos < bLen; aPos++, bPos++)
 
630
                        {
 
631
                                int cmp = (a[aPos] & unchecked((int)(0xff))) - (b[bPos] & unchecked((int)(0xff)));
 
632
                                if (cmp != 0)
 
633
                                {
 
634
                                        return cmp;
 
635
                                }
 
636
                        }
 
637
                        if (bPos == bLen)
 
638
                        {
 
639
                                return a[aPos] == '/' ? 0 : -1;
 
640
                        }
 
641
                        return aLen - bLen;
 
642
                }
 
643
 
 
644
                private static int Slash(byte[] a, int aPos)
 
645
                {
 
646
                        int aLen = a.Length;
 
647
                        for (; aPos < aLen; aPos++)
 
648
                        {
 
649
                                if (a[aPos] == '/')
 
650
                                {
 
651
                                        return aPos;
 
652
                                }
 
653
                        }
 
654
                        return -1;
 
655
                }
 
656
        }
 
657
}