/
BlobContentId.cs
132 lines (108 loc) · 4.73 KB
/
BlobContentId.cs
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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Internal;
namespace System.Reflection.Metadata
{
public readonly struct BlobContentId : IEquatable<BlobContentId>
{
private const int Size = BlobUtilities.SizeOfGuid + sizeof(uint);
private readonly Guid _guid;
private readonly uint _stamp;
public Guid Guid => _guid;
public uint Stamp => _stamp;
public BlobContentId(Guid guid, uint stamp)
{
_guid = guid;
_stamp = stamp;
}
public BlobContentId(ImmutableArray<byte> id)
{
if (id.IsDefault)
{
Throw.ArgumentNull(nameof(id));
}
Initialize(id.AsSpan(), out _guid, out _stamp);
}
public BlobContentId(byte[] id)
{
if (id is null)
{
Throw.ArgumentNull(nameof(id));
}
Initialize(id, out _guid, out _stamp);
}
private static unsafe void Initialize(ReadOnlySpan<byte> id, out Guid guid, out uint stamp)
{
if (id.Length != Size)
{
throw new ArgumentException(SR.Format(SR.UnexpectedArrayLength, Size), nameof(id));
}
fixed (byte* ptr = &id[0])
{
var reader = new BlobReader(ptr, id.Length);
guid = reader.ReadGuid();
stamp = reader.ReadUInt32();
}
}
public bool IsDefault => Guid == default(Guid) && Stamp == 0;
public static BlobContentId FromHash(ImmutableArray<byte> hashCode)
{
if (hashCode.IsDefault)
{
Throw.ArgumentNull(nameof(hashCode));
}
return FromHash(hashCode.AsSpan());
}
public static BlobContentId FromHash(byte[] hashCode)
{
if (hashCode is null)
{
Throw.ArgumentNull(nameof(hashCode));
}
return FromHash(hashCode.AsSpan());
}
private static BlobContentId FromHash(ReadOnlySpan<byte> hashCode)
{
const int minHashSize = 20;
if (hashCode.Length < minHashSize)
{
throw new ArgumentException(SR.Format(SR.HashTooShort, minHashSize), nameof(hashCode));
}
// extract guid components from input data
uint a = ((uint)hashCode[3] << 24 | (uint)hashCode[2] << 16 | (uint)hashCode[1] << 8 | hashCode[0]);
ushort b = (ushort)(hashCode[5] << 8 | hashCode[4]);
ushort c = (ushort)(hashCode[7] << 8 | hashCode[6]);
byte d = hashCode[8];
byte e = hashCode[9];
byte f = hashCode[10];
byte g = hashCode[11];
byte h = hashCode[12];
byte i = hashCode[13];
byte j = hashCode[14];
byte k = hashCode[15];
// modify the guid data so it decodes to the form of a "random" guid ala rfc4122
c = (ushort)((c & 0x0fff) | (4 << 12));
d = (byte)((d & 0x3f) | (2 << 6));
Guid guid = new Guid((int)a, (short)b, (short)c, d, e, f, g, h, i, j, k);
// compute a random-looking stamp from the remaining bits, but with the upper bit set
uint stamp = 0x80000000u | ((uint)hashCode[19] << 24 | (uint)hashCode[18] << 16 | (uint)hashCode[17] << 8 | hashCode[16]);
return new BlobContentId(guid, stamp);
}
public static Func<IEnumerable<Blob>, BlobContentId> GetTimeBasedProvider()
{
// In the PE File Header this is a "Time/Date Stamp" whose description is "Time and date
// the file was created in seconds since January 1st 1970 00:00:00 or 0"
// However, when we want to make it deterministic we fill it in (later) with bits from the hash of the full PE file.
uint timestamp = (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
return content => new BlobContentId(Guid.NewGuid(), timestamp);
}
public bool Equals(BlobContentId other) => Guid == other.Guid && Stamp == other.Stamp;
public override bool Equals([NotNullWhen(true)] object? obj) => obj is BlobContentId bcid && Equals(bcid);
public override int GetHashCode() => Hash.Combine(Stamp, Guid.GetHashCode());
public static bool operator ==(BlobContentId left, BlobContentId right) => left.Equals(right);
public static bool operator !=(BlobContentId left, BlobContentId right) => !left.Equals(right);
}
}