2016-12-08 4 views
0

나는 많은 것들을 해시해야한다 ... 그리고 해시를 일종의 콘텐츠 신원으로 유지한다. 나는이 모든 것을 사방에 사용한다. 해시는 20 바이트 배열이며 최근에 ToString() 메서드가있는 C# 프로젝트에서 (겉으로보기에는) 간단한 unsafe struct으로 변경했습니다. 그러나 런타임에 시각화는 항상 기본값 (모두 0)입니다. 콘텐츠가 변경된 후에도 마찬가지입니다.왜이 안전하지 않은 구조체에서 VS 2015 디버그 비주얼 라이저가 작동하지 않습니까?

구조체의 인스턴스 데이터는 고정 된 바이트 배열로 여러 가지 방법으로 기록됩니다. ToString() 메서드가 없다면, 비주얼 라이저는 값의 절름발이 표현을 보여 주었지만 고정 배열의 주소였습니다.

대체 방법으로 인해 시각화 프로그램이 기본 시각화에서 변경되지 않습니다. 예를 들어

:

toString() 메서드는 다음 생성하더라도

visualization

: 기대 값 (예상 시각화)이다

visualization2

....

나는 [DebuggerDisplay("{ToString()}")]을 시도해 본 결과를 직렬화 할 수 있지만 여전히 동일한 결과를 얻습니다. 그래서, 안전하지 않은 structs에 대해 운이 없다는 것을 의미합니까, 아니면 내가 정확히 지적하지 않은 잘못된 것을하고 있습니까?

편집 : 완전하고 검증 가능한 샘플에 넣어하지 않는

나의 사과. 다음은이 문제를 보여주는 완벽한 콘솔 앱입니다. Class1.cs의 내용을이 코드로 바꾸면 문제가 표시됩니다.

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Security.Cryptography; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
    static void Main(string[ ] args) 
    { 
     // sample data... 
     var bytes = new byte[ 20 ] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; 

     var hash1 = Hash.FromBytes(bytes); //deserailize a hash from array 
     Debug.WriteLine(hash1); 

     var hash2 = new Hash(bytes, 0, 20); //computes a hash over array 
     Debug.WriteLine(hash2); 

     using (var stream = new MemoryStream(bytes)) 
     { 
     var hash3 = new Hash();// empty hash 
     hash3.Read(stream); // deserialize a hash from stream 
     Debug.WriteLine(hash3); 

     stream.Position = 0; 

     var hash4 = new Hash(stream); //compute hash over stream 
     Debug.WriteLine(hash4); 

     var hash5 = new Hash("Compute the hash of a string"); 
     Debug.WriteLine(hash5); 

     Debug.Assert(hash1 == hash3, "Oops!"); 
     Debug.Assert(hash2 == hash4, "Nope!"); 
     Debug.Assert(hash1 != hash2, "Golly!"); 
     Debug.Assert(hash3 != hash4, "Shucks!"); 

     } 
    } 
    } 


    /// <summary>Represents a hash of a string or byte array</summary> 
    [StructLayout(LayoutKind.Sequential)] 
    public unsafe struct Hash: IComparable<Hash> 
    { 

    #region statics and constants 
    /// <summary>Character map for byte array to string</summary> 
    readonly static char[ ] hex = new char[ ] { 
       '0', '1', '2', '3', 
       '4', '5', '6', '7', 
       '8', '9', 'a', 'b', 
       'c', 'd', 'e', 'f' }; 

    /// <summary>Synchronization primitive</summary> 
    readonly static object sync = new object(); 

    /// <summary>Buffer for reading hashes from streams, strings, and arrays</summary> 
    readonly static byte[ ] buffer = new byte[ 20 ]; 

    /// <summary>ToString workspace</summary> 
    static char[ ] hexChars = new char[ Length * 2 ]; 

    /// <summary>Returns a hash that has no value</summary> 
    public readonly static Hash EmptyHash = new Hash(); 

    /// <summary>Retruns the length of any <see cref="Hash"/></summary> 
    public const int Length = 20; 


    /// <summary>Returns a <see cref="HashAlgorithm"/> that the system uses to compute hashes</summary> 
    public static HashAlgorithm GetHasher() 
    { 
     return new SHA1Managed(); 
    } 

    #endregion 

    #region private data 
    /// <summary>A pointer to the underlying data</summary> 
    fixed byte value[ 20 ]; 
    #endregion 

    #region construction 

    /// <summary>Creates a hash from a string</summary> 
    public Hash(string hashable) 
    { 
     fixed (byte* bytes = value, sourceBytes = GetHasher().ComputeHash(Encoding.Unicode.GetBytes(hashable))) 
     { 
     NativeMethods.CopyMemory(bytes, sourceBytes, Length); 
     } 
    } 

    /// <summary>Creates a hash from a byte array</summary> 
    public Hash(byte[ ] source, int index, int length) 
    { 
     fixed (byte* bytes = value, sourceBytes = GetHasher().ComputeHash(source, index, length)) 
     { 
     NativeMethods.CopyMemory(bytes, sourceBytes, Length); 
     } 
    } 

    /// <summary>Creates a hash from a series of hashes</summary> 
    public Hash(IEnumerable<Hash> hashes) 
    { 
     var hasher = GetHasher(); 
     var buffer = new byte[ Length ]; 
     hashes.Do(key => 
     { 
     key.CopyTo(buffer); 
     hasher.TransformBlock(buffer, 0, Length, buffer, 0); 
     }); 
     hasher.TransformFinalBlock(buffer, 0, 0); 
     fixed (byte* bytes = value, source = hasher.Hash) 
     { 
     NativeMethods.CopyMemory(bytes, source, Length); 
     } 
    } 


    /// <summary>Creates a hash over a stream from current position to end</summary> 
    public Hash(Stream stream) 
    { 
     const int bufferSize = 4096; 
     var hasher = GetHasher(); 
     var bytesRead = 0; 
     var buffer = new byte[ bufferSize ]; 
     while (true) 
     { 
     bytesRead = stream.Read(buffer, 0, bufferSize); 
     if (bytesRead == 0) 
     { 
      hasher.TransformFinalBlock(buffer, 0, 0); 
      break; 
     } 
     else 
     { 
      hasher.TransformBlock(buffer, 0, bytesRead, buffer, 0); 
     } 
     } 
     fixed (byte* bytes = value, source = hasher.Hash) 
     { 
     NativeMethods.CopyMemory(bytes, source, Length); 
     } 
    } 

    #endregion 


    #region methods 
    /// <summary>Copies the hash to the start of a byte array</summary> 
    public void CopyTo(byte[ ] buffer) 
    { 
     CopyTo(buffer, 0); 
    } 

    /// <summary>Copies the hash to a byte array</summary> 
    public void CopyTo(byte[ ] buffer, int offset) 
    { 
     if (buffer == null) throw new ArgumentNullException(nameof(buffer)); 
     if (buffer.Length < (offset + Length)) throw new ArgumentOutOfRangeException(nameof(buffer)); 
     fixed (byte* bytes = value, dest = buffer) 
     { 
     NativeMethods.CopyMemory(dest + offset, bytes, Length); 
     } 
    } 

    /// <summary>Returns a byte-array representation of the <see cref="Hash"/></summary> 
    /// <remarks>The returned value is a copy</remarks> 
    public byte[ ] GetBytes() 
    { 
     var results = new byte[ Length ]; 
     fixed (byte* bytes = value, target = results) 
     { 
     NativeMethods.CopyMemory(target, bytes, Length); 
     } 
     return results; 
    } 

    /// <summary>Compares this hash to another</summary> 
    public int CompareTo(Hash other) 
    { 
     var comparedByte = 0; 
     fixed (byte* bytes = value) 
     { 
     for (int i = 0; i < Length; i++) 
     { 
      comparedByte = (*(bytes + i)).CompareTo(other.value[ i ]); 
      if (comparedByte != 0) break; 
     } 
     return comparedByte; 
     } 
    } 

    /// <summary>Returns true if <paramref name="obj"/> is a <see cref="Hash"/> and it's value exactly matches</summary> 
    /// <param name="obj">The <see cref="Hash"/> to compare to this one</param> 
    /// <returns>true if the values match</returns> 
    public override bool Equals(object obj) 
    { 
     if (obj == null || !(obj is Hash)) return false; 
     var other = (Hash) obj; 
     return CompareTo(other) == 0; 
    } 

    /// <summary>Returns a .Net hash code for this <see cref="Hash"/></summary> 
    public override int GetHashCode() 
    { 
     unchecked 
     { 
     int hashCode = 17; 
     fixed (byte* bytes = value) 
     { 
      for (int i = 0; i < Length; i++) 
      { 
      hashCode = hashCode * 31 + *(bytes + i); 
      } 
      return hashCode; 
     } 
     } 
    } 

    /// <summary>Returns a hex string representation of the hash</summary> 
    public override string ToString() 
    { 
     lock (sync) 
     { 
     fixed (char* hexFixed = hex, hexCharsFixed = hexChars) 
     { 
      fixed (byte* bytes = value) 
      { 
      for (int i = 0; i < Length; i++) 
      { 
       *(hexCharsFixed + (i * 2)) = *(hexFixed + (*(bytes + i) >> 4)); 
       *(hexCharsFixed + (1 + (i * 2))) = *(hexFixed + (*(bytes + i) & 0xf)); 
      } 
      return new string(hexChars); 
      } 
     } 
     } 
    } 

    /// <summary>Reads a <see cref="Hash"/> from the provided stream</summary> 
    public void Read(Stream stream) 
    { 
     lock (sync) 
     { 
     var retryCount = 0; 
     var bytesRead = ReadStream(stream, buffer, 0, Length, ref retryCount); 
     if (bytesRead == Length) 
     { 
      fixed (byte* bytes = value, sourceBytes = buffer) 
      { 
      NativeMethods.CopyMemory(bytes, sourceBytes, Length); 
      } 
     } 
     } 
    } 

    /// <summary>Tries hard to populate a <see cref="Hash"/> from a stream - across multiple reads if necessary - up to a point</summary> 
    int ReadStream(Stream stream, byte[ ] buffer, int offset, int length, ref int retryCount) 
    { 
     const int maxStreamReadRetries = 3; 

     var bytesRead = stream.Read(buffer, offset, length); 
     var done = bytesRead == 0 || bytesRead == length; // eos, timeout, or success 
     if (!done) 
     { 
     if (retryCount++ >= maxStreamReadRetries) return 0; 
     bytesRead += ReadStream(stream, buffer, bytesRead, length - bytesRead, ref retryCount); 
     } 
     return bytesRead; 
    } 

    /// <summary>Writes the hash to a stream</summary> 
    public void Write(Stream stream) 
    { 
     lock (sync) 
     { 
     fixed (byte* bytes = value, targetBytes = buffer) 
     { 
      NativeMethods.CopyMemory(targetBytes, bytes, Length); 
     } 
     stream.Write(buffer, 0, Length); 
     } 
    } 

    /// <summary>Returns true if the hash has no value</summary> 
    public bool IsEmpty() 
    { 
     return Equals(EmptyHash); 
    } 

    /// <summary>Returns the result of XORing two <see cref="Hash"/>es</summary> 
    public static Hash Combine(Hash a, Hash b) 
    { 
     var results = new Hash(); 
     for (int i = 0; i < Length; i++) 
     { 
     *(results.value + i) = (byte) (*(a.value + i)^*(b.value + i)); 
     } 
     return results; 
    } 

    /// <summary>Returns the first non-empty hash from a list</summary> 
    public static Hash FirstNotEmpty(params Hash[ ] hashes) 
    { 
     foreach (var hash in hashes) if (!hash.IsEmpty()) return hash; 
     throw new ArgumentOutOfRangeException(nameof(hashes)); 
    } 

    /// <summary>Implements == operator</summary> 
    public static bool operator ==(Hash a, Hash b) 
    { 
     return a.Equals(b); 
    } 

    /// <summary>Implements != operator</summary> 
    public static bool operator !=(Hash a, Hash b) 
    { 
     return !a.Equals(b); 
    } 


    /// <summary>Converts a byte array to a <see cref="Hash"/></summary> 
    public static Hash FromBytes(byte[ ] hashBytes, int offset = 0) 
    { 
     if (hashBytes == null) throw new ArgumentNullException(nameof(hashBytes)); 
     if ((hashBytes.Length + offset) < Length) throw new ArgumentOutOfRangeException(nameof(hashBytes)); 
     var hash = new Hash(); 
     fixed (byte* sourceBytes = hashBytes) 
     NativeMethods.CopyMemory(hash.value, sourceBytes + offset, Length); 
     return hash; 
    } 
    #endregion 

    } 

    class NativeMethods 
    { 
    [DllImport("Kernel32", SetLastError = true, EntryPoint = "CopyMemory")] 
    internal unsafe static extern void CopyMemory(void* destination, void* source, uint length); 
    } 


    static class Extensions 
    { 
    /// <summary>Applies action to each element of the collection.</summary> 
    public static void Do<T>(this IEnumerable<T> enumerable, Action<T> action) 
    { 
     if (enumerable == null) throw new ArgumentNullException("enumerable"); 
     if (action == null) throw new ArgumentNullException("action"); 
     foreach (var item in enumerable) action(item); 
    } 
    } 
} 

메인 메소드의 끝 부분에 중단 점을 설정하고이 생성 한 후 hash5 변수를 통해 hash1의 위에 커서를 가져가 보자.

: 당신은 프로젝트 속성에서 안전하지 않은 코드 수 있도록 설정해야합니다. 도구 -> 옵션 -> 디버깅 -> 일반 -> "관리되는 호환 모드 사용"에서

+0

아마 관련 - http://stackoverflow.com/questions/34138112/a-pointer-type-static-fields-value-is-displayed-as-zero-0x0-by-the-debugger -whi – stuartd

+0

동의 함 - 답변이 있으면 멋있었을 것입니다. ;-) 나는 그것을 보았고 어쨌든 충분한 차이가 있다는 점에서 이것을 썼습니다. q의 주석 중 어느 것도 나를 유용하게 생각하지 않았습니다. – Clay

+0

@ Clay, 하나의 드라이브를 사용하여 전체 샘플을 공유 할 수 있습니까? 그래서 나는 같은 샘플을 사용하여 내 편에서 그것을 디버깅 할 수 있었다. –

답변

2

아래에 있습니다. 그것은 잘 작동합니다.

enter image description here

+0

대단히 감사합니다! 이 값들을 보는 것이 매우 좋습니다. – Clay

+0

+1, 내 문제도 해결합니다. 당신은 여기에 같은 대답을 넣을 수 있습니다 : [link] (http://stackoverflow.com/questions/34138112/a-pointer-type-static-fields-value-is-displayed-as-zero-0x0-by-the -debugger-whi) –