mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
042a9d2b2c
* Reorganize plugins per agreed structure (#271) Split the dotnet plugin into domain-specific plugins per discussion in #271: - dotnet: narrowed to common everyday C#/.NET coding tasks - dotnet-diag: perf investigations, debugging, incident analysis - dotnet-data: data access and Entity Framework - dotnet-aspnet, dotnet-wpf, dotnet-winforms, dotnet-maui: scaffolded empty to convey intent for future framework-specific skills Moved skills, tests, and CODEOWNERS entries accordingly. No file content was changed; all skill/test moves are pure renames. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Improve dotnet-diag and dotnet-winforms descriptions - dotnet-diag: add '.NET' qualifier to clarify scope - dotnet-winforms: use official 'Windows Forms (WinForms)' name Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add .NET qualifier to dotnet-data description Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove empty plugins; list them in CONTRIBUTING.md as future options Remove the four empty placeholder plugins (dotnet-aspnet, dotnet-wpf, dotnet-winforms, dotnet-maui) that had only plugin.json and no skills. Add a 'Plugin organization' section to CONTRIBUTING.md that describes the domain-specific plugin groupings introduced in this PR and lists the four removed plugin names as reserved candidates for future contributions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Create dotnet-upgrade plugin with migration/upgrade skills Move migrate-nullable-references, thread-abort-migration, and dotnet-aot-compat from dotnet to the new dotnet-upgrade plugin. Update CODEOWNERS paths (same owners), CONTRIBUTING.md plugin table, and fix the thread-abort-migration SKILL.md link in CONTRIBUTING.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
388 lines
14 KiB
C#
388 lines
14 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
#nullable enable
|
|
|
|
namespace Azure.Core
|
|
{
|
|
/// <summary>
|
|
/// Copied from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/HashCode.cs.
|
|
/// </summary>
|
|
internal struct HashCodeBuilder
|
|
{
|
|
private static readonly uint s_seed = GenerateGlobalSeed();
|
|
|
|
private const uint Prime1 = 2654435761U;
|
|
private const uint Prime2 = 2246822519U;
|
|
private const uint Prime3 = 3266489917U;
|
|
private const uint Prime4 = 668265263U;
|
|
private const uint Prime5 = 374761393U;
|
|
|
|
private uint _v1, _v2, _v3, _v4;
|
|
private uint _queue1, _queue2, _queue3;
|
|
private uint _length;
|
|
|
|
private static uint GenerateGlobalSeed()
|
|
{
|
|
return (uint)new Random().Next();
|
|
}
|
|
|
|
public static int Combine<T1>(T1 value1)
|
|
{
|
|
// Provide a way of diffusing bits from something with a limited
|
|
// input hash space. For example, many enums only have a few
|
|
// possible hashes, only using the bottom few bits of the code. Some
|
|
// collections are built on the assumption that hashes are spread
|
|
// over a larger space, so diffusing the bits may help the
|
|
// collection work more efficiently.
|
|
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
|
|
uint hash = MixEmptyState();
|
|
hash += 4;
|
|
|
|
hash = QueueRound(hash, hc1);
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
public static int Combine<T1, T2>(T1 value1, T2 value2)
|
|
{
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
|
|
|
uint hash = MixEmptyState();
|
|
hash += 8;
|
|
|
|
hash = QueueRound(hash, hc1);
|
|
hash = QueueRound(hash, hc2);
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
|
|
{
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
|
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
|
|
|
uint hash = MixEmptyState();
|
|
hash += 12;
|
|
|
|
hash = QueueRound(hash, hc1);
|
|
hash = QueueRound(hash, hc2);
|
|
hash = QueueRound(hash, hc3);
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
public static int Combine<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4)
|
|
{
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
|
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
|
uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
|
|
|
|
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
|
|
|
|
v1 = Round(v1, hc1);
|
|
v2 = Round(v2, hc2);
|
|
v3 = Round(v3, hc3);
|
|
v4 = Round(v4, hc4);
|
|
|
|
uint hash = MixState(v1, v2, v3, v4);
|
|
hash += 16;
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
public static int Combine<T1, T2, T3, T4, T5>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5)
|
|
{
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
|
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
|
uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
|
|
uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
|
|
|
|
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
|
|
|
|
v1 = Round(v1, hc1);
|
|
v2 = Round(v2, hc2);
|
|
v3 = Round(v3, hc3);
|
|
v4 = Round(v4, hc4);
|
|
|
|
uint hash = MixState(v1, v2, v3, v4);
|
|
hash += 20;
|
|
|
|
hash = QueueRound(hash, hc5);
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
public static int Combine<T1, T2, T3, T4, T5, T6>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6)
|
|
{
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
|
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
|
uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
|
|
uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
|
|
uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
|
|
|
|
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
|
|
|
|
v1 = Round(v1, hc1);
|
|
v2 = Round(v2, hc2);
|
|
v3 = Round(v3, hc3);
|
|
v4 = Round(v4, hc4);
|
|
|
|
uint hash = MixState(v1, v2, v3, v4);
|
|
hash += 24;
|
|
|
|
hash = QueueRound(hash, hc5);
|
|
hash = QueueRound(hash, hc6);
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
public static int Combine<T1, T2, T3, T4, T5, T6, T7>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7)
|
|
{
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
|
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
|
uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
|
|
uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
|
|
uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
|
|
uint hc7 = (uint)(value7?.GetHashCode() ?? 0);
|
|
|
|
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
|
|
|
|
v1 = Round(v1, hc1);
|
|
v2 = Round(v2, hc2);
|
|
v3 = Round(v3, hc3);
|
|
v4 = Round(v4, hc4);
|
|
|
|
uint hash = MixState(v1, v2, v3, v4);
|
|
hash += 28;
|
|
|
|
hash = QueueRound(hash, hc5);
|
|
hash = QueueRound(hash, hc6);
|
|
hash = QueueRound(hash, hc7);
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8)
|
|
{
|
|
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
|
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
|
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
|
uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
|
|
uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
|
|
uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
|
|
uint hc7 = (uint)(value7?.GetHashCode() ?? 0);
|
|
uint hc8 = (uint)(value8?.GetHashCode() ?? 0);
|
|
|
|
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
|
|
|
|
v1 = Round(v1, hc1);
|
|
v2 = Round(v2, hc2);
|
|
v3 = Round(v3, hc3);
|
|
v4 = Round(v4, hc4);
|
|
|
|
v1 = Round(v1, hc5);
|
|
v2 = Round(v2, hc6);
|
|
v3 = Round(v3, hc7);
|
|
v4 = Round(v4, hc8);
|
|
|
|
uint hash = MixState(v1, v2, v3, v4);
|
|
hash += 32;
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4)
|
|
{
|
|
v1 = s_seed + Prime1 + Prime2;
|
|
v2 = s_seed + Prime2;
|
|
v3 = s_seed;
|
|
v4 = s_seed - Prime1;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static uint Round(uint hash, uint input)
|
|
{
|
|
return RotateLeft(hash + input * Prime2, 13) * Prime1;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static uint QueueRound(uint hash, uint queuedValue)
|
|
{
|
|
return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static uint MixState(uint v1, uint v2, uint v3, uint v4)
|
|
{
|
|
return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static uint RotateLeft(uint value, int offset)
|
|
=> (value << offset) | (value >> (64 - offset));
|
|
|
|
private static uint MixEmptyState()
|
|
{
|
|
return s_seed + Prime5;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static uint MixFinal(uint hash)
|
|
{
|
|
hash ^= hash >> 15;
|
|
hash *= Prime2;
|
|
hash ^= hash >> 13;
|
|
hash *= Prime3;
|
|
hash ^= hash >> 16;
|
|
return hash;
|
|
}
|
|
|
|
public void Add<T>(T value)
|
|
{
|
|
Add(value?.GetHashCode() ?? 0);
|
|
}
|
|
|
|
public void Add<T>(T value, IEqualityComparer<T>? comparer)
|
|
{
|
|
Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode()));
|
|
}
|
|
|
|
private void Add(int value)
|
|
{
|
|
// The original xxHash works as follows:
|
|
// 0. Initialize immediately. We can't do this in a struct (no
|
|
// default ctor).
|
|
// 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators.
|
|
// 2. Accumulate remaining blocks of length 4 (1 uint) into the
|
|
// hash.
|
|
// 3. Accumulate remaining blocks of length 1 into the hash.
|
|
|
|
// There is no need for #3 as this type only accepts ints. _queue1,
|
|
// _queue2 and _queue3 are basically a buffer so that when
|
|
// ToHashCode is called we can execute #2 correctly.
|
|
|
|
// We need to initialize the xxHash32 state (_v1 to _v4) lazily (see
|
|
// #0) nd the last place that can be done if you look at the
|
|
// original code is just before the first block of 16 bytes is mixed
|
|
// in. The xxHash32 state is never used for streams containing fewer
|
|
// than 16 bytes.
|
|
|
|
// To see what's really going on here, have a look at the Combine
|
|
// methods.
|
|
|
|
uint val = (uint)value;
|
|
|
|
// Storing the value of _length locally shaves of quite a few bytes
|
|
// in the resulting machine code.
|
|
uint previousLength = _length++;
|
|
uint position = previousLength % 4;
|
|
|
|
// Switch can't be inlined.
|
|
|
|
if (position == 0)
|
|
_queue1 = val;
|
|
else if (position == 1)
|
|
_queue2 = val;
|
|
else if (position == 2)
|
|
_queue3 = val;
|
|
else // position == 3
|
|
{
|
|
if (previousLength == 3)
|
|
Initialize(out _v1, out _v2, out _v3, out _v4);
|
|
|
|
_v1 = Round(_v1, _queue1);
|
|
_v2 = Round(_v2, _queue2);
|
|
_v3 = Round(_v3, _queue3);
|
|
_v4 = Round(_v4, val);
|
|
}
|
|
}
|
|
|
|
public int ToHashCode()
|
|
{
|
|
// Storing the value of _length locally shaves of quite a few bytes
|
|
// in the resulting machine code.
|
|
uint length = _length;
|
|
|
|
// position refers to the *next* queue position in this method, so
|
|
// position == 1 means that _queue1 is populated; _queue2 would have
|
|
// been populated on the next call to Add.
|
|
uint position = length % 4;
|
|
|
|
// If the length is less than 4, _v1 to _v4 don't contain anything
|
|
// yet. xxHash32 treats this differently.
|
|
|
|
uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4);
|
|
|
|
// _length is incremented once per Add(Int32) and is therefore 4
|
|
// times too small (xxHash length is in bytes, not ints).
|
|
|
|
hash += length * 4;
|
|
|
|
// Mix what remains in the queue
|
|
|
|
// Switch can't be inlined right now, so use as few branches as
|
|
// possible by manually excluding impossible scenarios (position > 1
|
|
// is always false if position is not > 0).
|
|
if (position > 0)
|
|
{
|
|
hash = QueueRound(hash, _queue1);
|
|
if (position > 1)
|
|
{
|
|
hash = QueueRound(hash, _queue2);
|
|
if (position > 2)
|
|
hash = QueueRound(hash, _queue3);
|
|
}
|
|
}
|
|
|
|
hash = MixFinal(hash);
|
|
return (int)hash;
|
|
}
|
|
|
|
#pragma warning disable 0809
|
|
// Obsolete member 'memberA' overrides non-obsolete member 'memberB'.
|
|
// Disallowing GetHashCode and Equals is by design
|
|
|
|
// * We decided to not override GetHashCode() to produce the hash code
|
|
// as this would be weird, both naming-wise as well as from a
|
|
// behavioral standpoint (GetHashCode() should return the object's
|
|
// hash code, not the one being computed).
|
|
|
|
// * Even though ToHashCode() can be called safely multiple times on
|
|
// this implementation, it is not part of the contract. If the
|
|
// implementation has to change in the future we don't want to worry
|
|
// about people who might have incorrectly used this type.
|
|
|
|
[Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override int GetHashCode() => throw new NotSupportedException();
|
|
|
|
[Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override bool Equals(object? obj) => throw new NotSupportedException();
|
|
#pragma warning restore 0809
|
|
}
|
|
}
|