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

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit.Dircache/DirCacheEntry.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;
 
45
using System.IO;
 
46
using NGit;
 
47
using NGit.Util;
 
48
using Sharpen;
 
49
 
 
50
namespace NGit.Dircache
 
51
{
 
52
        /// <summary>
 
53
        /// A single file (or stage of a file) in a
 
54
        /// <see cref="DirCache">DirCache</see>
 
55
        /// .
 
56
        /// <p>
 
57
        /// An entry represents exactly one stage of a file. If a file path is unmerged
 
58
        /// then multiple DirCacheEntry instances may appear for the same path name.
 
59
        /// </summary>
 
60
        public class DirCacheEntry
 
61
        {
 
62
                private static readonly byte[] nullpad = new byte[8];
 
63
 
 
64
                /// <summary>The standard (fully merged) stage for an entry.</summary>
 
65
                /// <remarks>The standard (fully merged) stage for an entry.</remarks>
 
66
                public const int STAGE_0 = 0;
 
67
 
 
68
                /// <summary>The base tree revision for an entry.</summary>
 
69
                /// <remarks>The base tree revision for an entry.</remarks>
 
70
                public const int STAGE_1 = 1;
 
71
 
 
72
                /// <summary>The first tree revision (usually called "ours").</summary>
 
73
                /// <remarks>The first tree revision (usually called "ours").</remarks>
 
74
                public const int STAGE_2 = 2;
 
75
 
 
76
                /// <summary>The second tree revision (usually called "theirs").</summary>
 
77
                /// <remarks>The second tree revision (usually called "theirs").</remarks>
 
78
                public const int STAGE_3 = 3;
 
79
 
 
80
                private const int P_MTIME = 8;
 
81
 
 
82
                private const int P_MODE = 24;
 
83
 
 
84
                private const int P_SIZE = 36;
 
85
 
 
86
                private const int P_OBJECTID = 40;
 
87
 
 
88
                private const int P_FLAGS = 60;
 
89
 
 
90
                private const int P_FLAGS2 = 62;
 
91
 
 
92
                /// <summary>
 
93
                /// Mask applied to data in
 
94
                /// <see cref="P_FLAGS">P_FLAGS</see>
 
95
                /// to get the name length.
 
96
                /// </summary>
 
97
                private const int NAME_MASK = unchecked((int)(0xfff));
 
98
 
 
99
                private const int INTENT_TO_ADD = unchecked((int)(0x20000000));
 
100
 
 
101
                private const int SKIP_WORKTREE = unchecked((int)(0x40000000));
 
102
 
 
103
                private const int EXTENDED_FLAGS = (INTENT_TO_ADD | SKIP_WORKTREE);
 
104
 
 
105
                private const int INFO_LEN = 62;
 
106
 
 
107
                private const int INFO_LEN_EXTENDED = 64;
 
108
 
 
109
                private const int EXTENDED = unchecked((int)(0x40));
 
110
 
 
111
                private const int ASSUME_VALID = unchecked((int)(0x80));
 
112
 
 
113
                /// <summary>In-core flag signaling that the entry should be considered as modified.</summary>
 
114
                /// <remarks>In-core flag signaling that the entry should be considered as modified.</remarks>
 
115
                private const int UPDATE_NEEDED = unchecked((int)(0x1));
 
116
 
 
117
                /// <summary>(Possibly shared) header information storage.</summary>
 
118
                /// <remarks>(Possibly shared) header information storage.</remarks>
 
119
                private readonly byte[] info;
 
120
 
 
121
                /// <summary>
 
122
                /// First location within
 
123
                /// <see cref="info">info</see>
 
124
                /// where our header starts.
 
125
                /// </summary>
 
126
                private readonly int infoOffset;
 
127
 
 
128
                /// <summary>Our encoded path name, from the root of the repository.</summary>
 
129
                /// <remarks>Our encoded path name, from the root of the repository.</remarks>
 
130
                internal readonly byte[] path;
 
131
 
 
132
                /// <summary>Flags which are never stored to disk.</summary>
 
133
                /// <remarks>Flags which are never stored to disk.</remarks>
 
134
                private byte inCoreFlags;
 
135
 
 
136
                /// <exception cref="System.IO.IOException"></exception>
 
137
                internal DirCacheEntry(byte[] sharedInfo, MutableInteger infoAt, InputStream @in, 
 
138
                        MessageDigest md)
 
139
                {
 
140
                        // private static final int P_CTIME = 0;
 
141
                        // private static final int P_CTIME_NSEC = 4;
 
142
                        // private static final int P_MTIME_NSEC = 12;
 
143
                        // private static final int P_DEV = 16;
 
144
                        // private static final int P_INO = 20;
 
145
                        // private static final int P_UID = 28;
 
146
                        // private static final int P_GID = 32;
 
147
                        info = sharedInfo;
 
148
                        infoOffset = infoAt.value;
 
149
                        IOUtil.ReadFully(@in, info, infoOffset, INFO_LEN);
 
150
                        int len;
 
151
                        if (IsExtended)
 
152
                        {
 
153
                                len = INFO_LEN_EXTENDED;
 
154
                                IOUtil.ReadFully(@in, info, infoOffset + INFO_LEN, INFO_LEN_EXTENDED - INFO_LEN);
 
155
                                if ((GetExtendedFlags() & ~EXTENDED_FLAGS) != 0)
 
156
                                {
 
157
                                        throw new IOException(MessageFormat.Format(JGitText.Get().DIRCUnrecognizedExtendedFlags
 
158
                                                , GetExtendedFlags().ToString()));
 
159
                                }
 
160
                        }
 
161
                        else
 
162
                        {
 
163
                                len = INFO_LEN;
 
164
                        }
 
165
                        infoAt.value += len;
 
166
                        md.Update(info, infoOffset, len);
 
167
                        int pathLen = NB.DecodeUInt16(info, infoOffset + P_FLAGS) & NAME_MASK;
 
168
                        int skipped = 0;
 
169
                        if (pathLen < NAME_MASK)
 
170
                        {
 
171
                                path = new byte[pathLen];
 
172
                                IOUtil.ReadFully(@in, path, 0, pathLen);
 
173
                                md.Update(path, 0, pathLen);
 
174
                        }
 
175
                        else
 
176
                        {
 
177
                                ByteArrayOutputStream tmp = new ByteArrayOutputStream();
 
178
                                {
 
179
                                        byte[] buf = new byte[NAME_MASK];
 
180
                                        IOUtil.ReadFully(@in, buf, 0, NAME_MASK);
 
181
                                        tmp.Write(buf);
 
182
                                }
 
183
                                for (; ; )
 
184
                                {
 
185
                                        int c = @in.Read();
 
186
                                        if (c < 0)
 
187
                                        {
 
188
                                                throw new EOFException(JGitText.Get().shortReadOfBlock);
 
189
                                        }
 
190
                                        if (c == 0)
 
191
                                        {
 
192
                                                break;
 
193
                                        }
 
194
                                        tmp.Write(c);
 
195
                                }
 
196
                                path = tmp.ToByteArray();
 
197
                                pathLen = path.Length;
 
198
                                skipped = 1;
 
199
                                // we already skipped 1 '\0' above to break the loop.
 
200
                                md.Update(path, 0, pathLen);
 
201
                                md.Update(unchecked((byte)0));
 
202
                        }
 
203
                        // Index records are padded out to the next 8 byte alignment
 
204
                        // for historical reasons related to how C Git read the files.
 
205
                        //
 
206
                        int actLen = len + pathLen;
 
207
                        int expLen = (actLen + 8) & ~7;
 
208
                        int padLen = expLen - actLen - skipped;
 
209
                        if (padLen > 0)
 
210
                        {
 
211
                                IOUtil.SkipFully(@in, padLen);
 
212
                                md.Update(nullpad, 0, padLen);
 
213
                        }
 
214
                }
 
215
 
 
216
                /// <summary>Create an empty entry at stage 0.</summary>
 
217
                /// <remarks>Create an empty entry at stage 0.</remarks>
 
218
                /// <param name="newPath">name of the cache entry.</param>
 
219
                /// <exception cref="System.ArgumentException">
 
220
                /// If the path starts or ends with "/", or contains "//" either
 
221
                /// "\0". These sequences are not permitted in a git tree object
 
222
                /// or DirCache file.
 
223
                /// </exception>
 
224
                public DirCacheEntry(string newPath) : this(Constants.Encode(newPath))
 
225
                {
 
226
                }
 
227
 
 
228
                /// <summary>Create an empty entry at the specified stage.</summary>
 
229
                /// <remarks>Create an empty entry at the specified stage.</remarks>
 
230
                /// <param name="newPath">name of the cache entry.</param>
 
231
                /// <param name="stage">the stage index of the new entry.</param>
 
232
                /// <exception cref="System.ArgumentException">
 
233
                /// If the path starts or ends with "/", or contains "//" either
 
234
                /// "\0". These sequences are not permitted in a git tree object
 
235
                /// or DirCache file.  Or if
 
236
                /// <code>stage</code>
 
237
                /// is outside of the
 
238
                /// range 0..3, inclusive.
 
239
                /// </exception>
 
240
                public DirCacheEntry(string newPath, int stage) : this(Constants.Encode(newPath), 
 
241
                        stage)
 
242
                {
 
243
                }
 
244
 
 
245
                /// <summary>Create an empty entry at stage 0.</summary>
 
246
                /// <remarks>Create an empty entry at stage 0.</remarks>
 
247
                /// <param name="newPath">name of the cache entry, in the standard encoding.</param>
 
248
                /// <exception cref="System.ArgumentException">
 
249
                /// If the path starts or ends with "/", or contains "//" either
 
250
                /// "\0". These sequences are not permitted in a git tree object
 
251
                /// or DirCache file.
 
252
                /// </exception>
 
253
                public DirCacheEntry(byte[] newPath) : this(newPath, STAGE_0)
 
254
                {
 
255
                }
 
256
 
 
257
                /// <summary>Create an empty entry at the specified stage.</summary>
 
258
                /// <remarks>Create an empty entry at the specified stage.</remarks>
 
259
                /// <param name="newPath">name of the cache entry, in the standard encoding.</param>
 
260
                /// <param name="stage">the stage index of the new entry.</param>
 
261
                /// <exception cref="System.ArgumentException">
 
262
                /// If the path starts or ends with "/", or contains "//" either
 
263
                /// "\0". These sequences are not permitted in a git tree object
 
264
                /// or DirCache file.  Or if
 
265
                /// <code>stage</code>
 
266
                /// is outside of the
 
267
                /// range 0..3, inclusive.
 
268
                /// </exception>
 
269
                public DirCacheEntry(byte[] newPath, int stage)
 
270
                {
 
271
                        if (!IsValidPath(newPath))
 
272
                        {
 
273
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().invalidPath, ToString
 
274
                                        (newPath)));
 
275
                        }
 
276
                        if (stage < 0 || 3 < stage)
 
277
                        {
 
278
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().invalidStageForPath
 
279
                                        , stage, ToString(newPath)));
 
280
                        }
 
281
                        info = new byte[INFO_LEN];
 
282
                        infoOffset = 0;
 
283
                        path = newPath;
 
284
                        int flags = ((stage & unchecked((int)(0x3))) << 12);
 
285
                        if (path.Length < NAME_MASK)
 
286
                        {
 
287
                                flags |= path.Length;
 
288
                        }
 
289
                        else
 
290
                        {
 
291
                                flags |= NAME_MASK;
 
292
                        }
 
293
                        NB.EncodeInt16(info, infoOffset + P_FLAGS, flags);
 
294
                }
 
295
 
 
296
                /// <exception cref="System.IO.IOException"></exception>
 
297
                internal virtual void Write(OutputStream os)
 
298
                {
 
299
                        int len = IsExtended ? INFO_LEN_EXTENDED : INFO_LEN;
 
300
                        int pathLen = path.Length;
 
301
                        os.Write(info, infoOffset, len);
 
302
                        os.Write(path, 0, pathLen);
 
303
                        // Index records are padded out to the next 8 byte alignment
 
304
                        // for historical reasons related to how C Git read the files.
 
305
                        //
 
306
                        int actLen = len + pathLen;
 
307
                        int expLen = (actLen + 8) & ~7;
 
308
                        if (actLen != expLen)
 
309
                        {
 
310
                                os.Write(nullpad, 0, expLen - actLen);
 
311
                        }
 
312
                }
 
313
 
 
314
                /// <summary>
 
315
                /// Is it possible for this entry to be accidentally assumed clean?
 
316
                /// <p>
 
317
                /// The "racy git" problem happens when a work file can be updated faster
 
318
                /// than the filesystem records file modification timestamps.
 
319
                /// </summary>
 
320
                /// <remarks>
 
321
                /// Is it possible for this entry to be accidentally assumed clean?
 
322
                /// <p>
 
323
                /// The "racy git" problem happens when a work file can be updated faster
 
324
                /// than the filesystem records file modification timestamps. It is possible
 
325
                /// for an application to edit a work file, update the index, then edit it
 
326
                /// again before the filesystem will give the work file a new modification
 
327
                /// timestamp. This method tests to see if file was written out at the same
 
328
                /// time as the index.
 
329
                /// </remarks>
 
330
                /// <param name="smudge_s">seconds component of the index's last modified time.</param>
 
331
                /// <param name="smudge_ns">nanoseconds component of the index's last modified time.</param>
 
332
                /// <returns>true if extra careful checks should be used.</returns>
 
333
                public bool MightBeRacilyClean(int smudge_s, int smudge_ns)
 
334
                {
 
335
                        // If the index has a modification time then it came from disk
 
336
                        // and was not generated from scratch in memory. In such cases
 
337
                        // the entry is 'racily clean' if the entry's cached modification
 
338
                        // time is equal to or later than the index modification time. In
 
339
                        // such cases the work file is too close to the index to tell if
 
340
                        // it is clean or not based on the modification time alone.
 
341
                        //
 
342
                        int @base = infoOffset + P_MTIME;
 
343
                        int mtime = NB.DecodeInt32(info, @base);
 
344
                        if (smudge_s == mtime)
 
345
                        {
 
346
                                return smudge_ns <= NB.DecodeInt32(info, @base + 4);
 
347
                        }
 
348
                        return false;
 
349
                }
 
350
 
 
351
                /// <summary>Force this entry to no longer match its working tree file.</summary>
 
352
                /// <remarks>
 
353
                /// Force this entry to no longer match its working tree file.
 
354
                /// <p>
 
355
                /// This avoids the "racy git" problem by making this index entry no longer
 
356
                /// match the file in the working directory. Later git will be forced to
 
357
                /// compare the file content to ensure the file matches the working tree.
 
358
                /// </remarks>
 
359
                public void SmudgeRacilyClean()
 
360
                {
 
361
                        // To mark an entry racily clean we set its length to 0 (like native git
 
362
                        // does). Entries which are not racily clean and have zero length can be
 
363
                        // distinguished from racily clean entries by checking P_OBJECTID
 
364
                        // against the SHA1 of empty content. When length is 0 and P_OBJECTID is
 
365
                        // different from SHA1 of empty content we know the entry is marked
 
366
                        // racily clean
 
367
                        int @base = infoOffset + P_SIZE;
 
368
                        Arrays.Fill(info, @base, @base + 4, unchecked((byte)0));
 
369
                }
 
370
 
 
371
                /// <summary>
 
372
                /// Check whether this entry has been smudged or not
 
373
                /// <p>
 
374
                /// If a blob has length 0 we know his id see
 
375
                /// <see cref="NGit.Constants.EMPTY_BLOB_ID">NGit.Constants.EMPTY_BLOB_ID</see>
 
376
                /// . If an entry
 
377
                /// has length 0 and an ID different from the one for empty blob we know this
 
378
                /// entry was smudged.
 
379
                /// </summary>
 
380
                /// <returns>
 
381
                /// <code>true</code> if the entry is smudged, <code>false</code>
 
382
                /// otherwise
 
383
                /// </returns>
 
384
                public bool IsSmudged
 
385
                {
 
386
                        get
 
387
                        {
 
388
                                int @base = infoOffset + P_OBJECTID;
 
389
                                return (Length == 0) && (Constants.EMPTY_BLOB_ID.CompareTo(info, @base) != 0);
 
390
                        }
 
391
                }
 
392
 
 
393
                internal byte[] IdBuffer
 
394
                {
 
395
                        get
 
396
                        {
 
397
                                return info;
 
398
                        }
 
399
                }
 
400
 
 
401
                internal int IdOffset
 
402
                {
 
403
                        get
 
404
                        {
 
405
                                return infoOffset + P_OBJECTID;
 
406
                        }
 
407
                }
 
408
 
 
409
                /// <summary>
 
410
                /// Is this entry always thought to be unmodified?
 
411
                /// <p>
 
412
                /// Most entries in the index do not have this flag set.
 
413
                /// </summary>
 
414
                /// <remarks>
 
415
                /// Is this entry always thought to be unmodified?
 
416
                /// <p>
 
417
                /// Most entries in the index do not have this flag set. Users may however
 
418
                /// set them on if the file system stat() costs are too high on this working
 
419
                /// directory, such as on NFS or SMB volumes.
 
420
                /// </remarks>
 
421
                /// <returns>true if we must assume the entry is unmodified.</returns>
 
422
                /// <summary>Set the assume valid flag for this entry,</summary>
 
423
                /// <value>
 
424
                /// true to ignore apparent modifications; false to look at last
 
425
                /// modified to detect file modifications.
 
426
                /// </value>
 
427
                public virtual bool IsAssumeValid
 
428
                {
 
429
                        get
 
430
                        {
 
431
                                return (info[infoOffset + P_FLAGS] & ASSUME_VALID) != 0;
 
432
                        }
 
433
                        set
 
434
                        {
 
435
                                bool assume = value;
 
436
                                if (assume)
 
437
                                {
 
438
                                        info[infoOffset + P_FLAGS] |= ASSUME_VALID;
 
439
                                }
 
440
                                else
 
441
                                {
 
442
                                        info[infoOffset + P_FLAGS] &= unchecked((byte)~ASSUME_VALID);
 
443
                                }
 
444
                        }
 
445
                }
 
446
 
 
447
                /// <returns>true if this entry should be checked for changes</returns>
 
448
                /// <summary>Set whether this entry must be checked for changes</summary>
 
449
                /// <value></value>
 
450
                public virtual bool IsUpdateNeeded
 
451
                {
 
452
                        get
 
453
                        {
 
454
                                return (inCoreFlags & UPDATE_NEEDED) != 0;
 
455
                        }
 
456
                        set
 
457
                        {
 
458
                                bool updateNeeded = value;
 
459
                                if (updateNeeded)
 
460
                                {
 
461
                                        inCoreFlags |= UPDATE_NEEDED;
 
462
                                }
 
463
                                else
 
464
                                {
 
465
                                        inCoreFlags &= unchecked((byte)~UPDATE_NEEDED);
 
466
                                }
 
467
                        }
 
468
                }
 
469
 
 
470
                /// <summary>Get the stage of this entry.</summary>
 
471
                /// <remarks>
 
472
                /// Get the stage of this entry.
 
473
                /// <p>
 
474
                /// Entries have one of 4 possible stages: 0-3.
 
475
                /// </remarks>
 
476
                /// <returns>the stage of this entry.</returns>
 
477
                public virtual int Stage
 
478
                {
 
479
                        get
 
480
                        {
 
481
                                return (info[infoOffset + P_FLAGS] >> 4) & unchecked((int)(0x3));
 
482
                        }
 
483
                }
 
484
 
 
485
                /// <summary>Returns whether this entry should be skipped from the working tree.</summary>
 
486
                /// <remarks>Returns whether this entry should be skipped from the working tree.</remarks>
 
487
                /// <returns>true if this entry should be skipepd.</returns>
 
488
                public virtual bool IsSkipWorkTree
 
489
                {
 
490
                        get
 
491
                        {
 
492
                                return (GetExtendedFlags() & SKIP_WORKTREE) != 0;
 
493
                        }
 
494
                }
 
495
 
 
496
                /// <summary>Returns whether this entry is intent to be added to the Index.</summary>
 
497
                /// <remarks>Returns whether this entry is intent to be added to the Index.</remarks>
 
498
                /// <returns>true if this entry is intent to add.</returns>
 
499
                public virtual bool IsIntentToAdd
 
500
                {
 
501
                        get
 
502
                        {
 
503
                                return (GetExtendedFlags() & INTENT_TO_ADD) != 0;
 
504
                        }
 
505
                }
 
506
 
 
507
                /// <summary>
 
508
                /// Obtain the raw
 
509
                /// <see cref="NGit.FileMode">NGit.FileMode</see>
 
510
                /// bits for this entry.
 
511
                /// </summary>
 
512
                /// <returns>mode bits for the entry.</returns>
 
513
                /// <seealso cref="NGit.FileMode.FromBits(int)">NGit.FileMode.FromBits(int)</seealso>
 
514
                public virtual int RawMode
 
515
                {
 
516
                        get
 
517
                        {
 
518
                                return NB.DecodeInt32(info, infoOffset + P_MODE);
 
519
                        }
 
520
                }
 
521
 
 
522
                /// <summary>
 
523
                /// Obtain the
 
524
                /// <see cref="NGit.FileMode">NGit.FileMode</see>
 
525
                /// for this entry.
 
526
                /// </summary>
 
527
                /// <returns>the file mode singleton for this entry.</returns>
 
528
                /// <summary>Set the file mode for this entry.</summary>
 
529
                /// <remarks>Set the file mode for this entry.</remarks>
 
530
                /// <value>the new mode constant.</value>
 
531
                /// <exception cref="System.ArgumentException">
 
532
                /// If
 
533
                /// <code>mode</code>
 
534
                /// is
 
535
                /// <see cref="NGit.FileMode.MISSING">NGit.FileMode.MISSING</see>
 
536
                /// ,
 
537
                /// <see cref="NGit.FileMode.TREE">NGit.FileMode.TREE</see>
 
538
                /// , or any other type code not permitted
 
539
                /// in a tree object.
 
540
                /// </exception>
 
541
                public virtual NGit.FileMode FileMode
 
542
                {
 
543
                        get
 
544
                        {
 
545
                                return NGit.FileMode.FromBits(RawMode);
 
546
                        }
 
547
                        set
 
548
                        {
 
549
                                NGit.FileMode mode = value;
 
550
                                switch (mode.GetBits() & NGit.FileMode.TYPE_MASK)
 
551
                                {
 
552
                                        case NGit.FileMode.TYPE_MISSING:
 
553
                                        case NGit.FileMode.TYPE_TREE:
 
554
                                        {
 
555
                                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().invalidModeForPath
 
556
                                                        , mode, PathString));
 
557
                                        }
 
558
                                }
 
559
                                NB.EncodeInt32(info, infoOffset + P_MODE, mode.GetBits());
 
560
                        }
 
561
                }
 
562
 
 
563
                /// <summary>Get the cached last modification date of this file, in milliseconds.</summary>
 
564
                /// <remarks>
 
565
                /// Get the cached last modification date of this file, in milliseconds.
 
566
                /// <p>
 
567
                /// One of the indicators that the file has been modified by an application
 
568
                /// changing the working tree is if the last modification time for the file
 
569
                /// differs from the time stored in this entry.
 
570
                /// </remarks>
 
571
                /// <returns>
 
572
                /// last modification time of this file, in milliseconds since the
 
573
                /// Java epoch (midnight Jan 1, 1970 UTC).
 
574
                /// </returns>
 
575
                /// <summary>Set the cached last modification date of this file, using milliseconds.</summary>
 
576
                /// <remarks>Set the cached last modification date of this file, using milliseconds.</remarks>
 
577
                /// <value>new cached modification date of the file, in milliseconds.</value>
 
578
                public virtual long LastModified
 
579
                {
 
580
                        get
 
581
                        {
 
582
                                return DecodeTS(P_MTIME);
 
583
                        }
 
584
                        set
 
585
                        {
 
586
                                long when = value;
 
587
                                EncodeTS(P_MTIME, when);
 
588
                        }
 
589
                }
 
590
 
 
591
                /// <summary>Get the cached size (in bytes) of this file.</summary>
 
592
                /// <remarks>
 
593
                /// Get the cached size (in bytes) of this file.
 
594
                /// <p>
 
595
                /// One of the indicators that the file has been modified by an application
 
596
                /// changing the working tree is if the size of the file (in bytes) differs
 
597
                /// from the size stored in this entry.
 
598
                /// <p>
 
599
                /// Note that this is the length of the file in the working directory, which
 
600
                /// may differ from the size of the decompressed blob if work tree filters
 
601
                /// are being used, such as LF<->CRLF conversion.
 
602
                /// </remarks>
 
603
                /// <returns>cached size of the working directory file, in bytes.</returns>
 
604
                public virtual int Length
 
605
                {
 
606
                        get
 
607
                        {
 
608
                                return NB.DecodeInt32(info, infoOffset + P_SIZE);
 
609
                        }
 
610
                }
 
611
 
 
612
                /// <summary>Set the cached size (in bytes) of this file.</summary>
 
613
                /// <remarks>Set the cached size (in bytes) of this file.</remarks>
 
614
                /// <param name="sz">new cached size of the file, as bytes.</param>
 
615
                public virtual void SetLength(int sz)
 
616
                {
 
617
                        NB.EncodeInt32(info, infoOffset + P_SIZE, sz);
 
618
                }
 
619
 
 
620
                /// <summary>Set the cached size (in bytes) of this file.</summary>
 
621
                /// <remarks>Set the cached size (in bytes) of this file.</remarks>
 
622
                /// <param name="sz">new cached size of the file, as bytes.</param>
 
623
                /// <exception cref="System.ArgumentException">
 
624
                /// if the size exceeds the 2 GiB barrier imposed by current file
 
625
                /// format limitations.
 
626
                /// </exception>
 
627
                public virtual void SetLength(long sz)
 
628
                {
 
629
                        if (int.MaxValue <= sz)
 
630
                        {
 
631
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().sizeExceeds2GB, PathString
 
632
                                        , sz));
 
633
                        }
 
634
                        SetLength((int)sz);
 
635
                }
 
636
 
 
637
                /// <summary>Obtain the ObjectId for the entry.</summary>
 
638
                /// <remarks>
 
639
                /// Obtain the ObjectId for the entry.
 
640
                /// <p>
 
641
                /// Using this method to compare ObjectId values between entries is
 
642
                /// inefficient as it causes memory allocation.
 
643
                /// </remarks>
 
644
                /// <returns>object identifier for the entry.</returns>
 
645
                public virtual ObjectId GetObjectId()
 
646
                {
 
647
                        return ObjectId.FromRaw(IdBuffer, IdOffset);
 
648
                }
 
649
 
 
650
                /// <summary>Set the ObjectId for the entry.</summary>
 
651
                /// <remarks>Set the ObjectId for the entry.</remarks>
 
652
                /// <param name="id">
 
653
                /// new object identifier for the entry. May be
 
654
                /// <see cref="NGit.ObjectId.ZeroId()">NGit.ObjectId.ZeroId()</see>
 
655
                /// to remove the current identifier.
 
656
                /// </param>
 
657
                public virtual void SetObjectId(AnyObjectId id)
 
658
                {
 
659
                        id.CopyRawTo(IdBuffer, IdOffset);
 
660
                }
 
661
 
 
662
                /// <summary>Set the ObjectId for the entry from the raw binary representation.</summary>
 
663
                /// <remarks>Set the ObjectId for the entry from the raw binary representation.</remarks>
 
664
                /// <param name="bs">
 
665
                /// the raw byte buffer to read from. At least 20 bytes after p
 
666
                /// must be available within this byte array.
 
667
                /// </param>
 
668
                /// <param name="p">position to read the first byte of data from.</param>
 
669
                public virtual void SetObjectIdFromRaw(byte[] bs, int p)
 
670
                {
 
671
                        int n = Constants.OBJECT_ID_LENGTH;
 
672
                        System.Array.Copy(bs, p, IdBuffer, IdOffset, n);
 
673
                }
 
674
 
 
675
                /// <summary>Get the entry's complete path.</summary>
 
676
                /// <remarks>
 
677
                /// Get the entry's complete path.
 
678
                /// <p>
 
679
                /// This method is not very efficient and is primarily meant for debugging
 
680
                /// and final output generation. Applications should try to avoid calling it,
 
681
                /// and if invoked do so only once per interesting entry, where the name is
 
682
                /// absolutely required for correct function.
 
683
                /// </remarks>
 
684
                /// <returns>
 
685
                /// complete path of the entry, from the root of the repository. If
 
686
                /// the entry is in a subtree there will be at least one '/' in the
 
687
                /// returned string.
 
688
                /// </returns>
 
689
                public virtual string PathString
 
690
                {
 
691
                        get
 
692
                        {
 
693
                                return ToString(path);
 
694
                        }
 
695
                }
 
696
 
 
697
                /// <summary>Copy the ObjectId and other meta fields from an existing entry.</summary>
 
698
                /// <remarks>
 
699
                /// Copy the ObjectId and other meta fields from an existing entry.
 
700
                /// <p>
 
701
                /// This method copies everything except the path from one entry to another,
 
702
                /// supporting renaming.
 
703
                /// </remarks>
 
704
                /// <param name="src">the entry to copy ObjectId and meta fields from.</param>
 
705
                public virtual void CopyMetaData(NGit.Dircache.DirCacheEntry src)
 
706
                {
 
707
                        int pLen = NB.DecodeUInt16(info, infoOffset + P_FLAGS) & NAME_MASK;
 
708
                        System.Array.Copy(src.info, src.infoOffset, info, infoOffset, INFO_LEN);
 
709
                        NB.EncodeInt16(info, infoOffset + P_FLAGS, pLen | NB.DecodeUInt16(info, infoOffset
 
710
                                 + P_FLAGS) & ~NAME_MASK);
 
711
                }
 
712
 
 
713
                /// <returns>true if the entry contains extended flags.</returns>
 
714
                internal virtual bool IsExtended
 
715
                {
 
716
                        get
 
717
                        {
 
718
                                return (info[infoOffset + P_FLAGS] & EXTENDED) != 0;
 
719
                        }
 
720
                }
 
721
 
 
722
                private long DecodeTS(int pIdx)
 
723
                {
 
724
                        int @base = infoOffset + pIdx;
 
725
                        int sec = NB.DecodeInt32(info, @base);
 
726
                        int ms = NB.DecodeInt32(info, @base + 4) / 1000000;
 
727
                        return 1000L * sec + ms;
 
728
                }
 
729
 
 
730
                private void EncodeTS(int pIdx, long when)
 
731
                {
 
732
                        int @base = infoOffset + pIdx;
 
733
                        NB.EncodeInt32(info, @base, (int)(when / 1000));
 
734
                        NB.EncodeInt32(info, @base + 4, ((int)(when % 1000)) * 1000000);
 
735
                }
 
736
 
 
737
                private int GetExtendedFlags()
 
738
                {
 
739
                        if (IsExtended)
 
740
                        {
 
741
                                return NB.DecodeUInt16(info, infoOffset + P_FLAGS2) << 16;
 
742
                        }
 
743
                        else
 
744
                        {
 
745
                                return 0;
 
746
                        }
 
747
                }
 
748
 
 
749
                private static string ToString(byte[] path)
 
750
                {
 
751
                        return Constants.CHARSET.Decode(ByteBuffer.Wrap(path)).ToString();
 
752
                }
 
753
 
 
754
                internal static bool IsValidPath(byte[] path)
 
755
                {
 
756
                        if (path.Length == 0)
 
757
                        {
 
758
                                return false;
 
759
                        }
 
760
                        // empty path is not permitted.
 
761
                        bool componentHasChars = false;
 
762
                        foreach (byte c in path)
 
763
                        {
 
764
                                switch (c)
 
765
                                {
 
766
                                        case 0:
 
767
                                        {
 
768
                                                return false;
 
769
                                        }
 
770
 
 
771
                                        case (byte)('/'):
 
772
                                        {
 
773
                                                // NUL is never allowed within the path.
 
774
                                                if (componentHasChars)
 
775
                                                {
 
776
                                                        componentHasChars = false;
 
777
                                                }
 
778
                                                else
 
779
                                                {
 
780
                                                        return false;
 
781
                                                }
 
782
                                                break;
 
783
                                        }
 
784
 
 
785
                                        default:
 
786
                                        {
 
787
                                                componentHasChars = true;
 
788
                                                break;
 
789
                                        }
 
790
                                }
 
791
                        }
 
792
                        return componentHasChars;
 
793
                }
 
794
 
 
795
                internal static int GetMaximumInfoLength(bool extended)
 
796
                {
 
797
                        return extended ? INFO_LEN_EXTENDED : INFO_LEN;
 
798
                }
 
799
        }
 
800
}