2017-09-06 23 views
0

我們有一個用.NET編寫的遺留系統,我們正在遷移到Node.js.手動實現String.GetHashCode()?

原始系統使用("some string value").GetHashCode()根據用戶數據生成一些令牌。

我正在尋找一種方法來在JavaScript中實現這個功能,以便將這部分系統移植到Node.js.

因此,我感興趣的是String.GetHashCode()實際上是如何工作的。有什麼地方記錄算法嗎?它甚至是一個穩定的算法,還是它跨各種.NET版本改變?

我試圖找到它的實現的一些細節,但對我來說真的很困難,因爲.NET並不是我的主要技術,我也不是很熟悉它的資源和信息來源。

+1

.NET中的哈希碼在各個版本中都不穩定。 [這是來自.net核心的一個實現](https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs) –

+0

跨版本的穩定性有關係嗎?它只是在程序運行時產生相同的值,不是? – Icepickle

+0

從[源代碼](https://referencesource.microsoft.com/#mscorlib/system/string.cs)中的註釋,不僅在.net版本中不穩定,它在AppDomain之間甚至可能不穩定相同的過程。 –

回答

1

從微軟的Reference Source兩者,一個實現是:

 // Gets a hash code for this string. If strings A and B are such that A.Equals(B), then 
     // they will return the same hash code. 
     [System.Security.SecuritySafeCritical] // auto-generated 
     [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
     public override int GetHashCode() { 

#if FEATURE_RANDOMIZED_STRING_HASHING 
      if(HashHelpers.s_UseRandomizedStringHashing) 
      { 
       return InternalMarvin32HashString(this, this.Length, 0); 
      } 
#endif // FEATURE_RANDOMIZED_STRING_HASHING 

      unsafe { 
       fixed (char *src = this) { 
        Contract.Assert(src[this.Length] == '\0', "src[this.Length] == '\\0'"); 
        Contract.Assert(((int)src)%4 == 0, "Managed string should start at 4 bytes boundary"); 

#if WIN32 
        int hash1 = (5381<<16) + 5381; 
#else 
        int hash1 = 5381; 
#endif 
        int hash2 = hash1; 

#if WIN32 
        // 32 bit machines. 
        int* pint = (int *)src; 
        int len = this.Length; 
        while (len > 2) 
        { 
         hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27))^pint[0]; 
         hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27))^pint[1]; 
         pint += 2; 
         len -= 4; 
        } 

        if (len > 0) 
        { 
         hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27))^pint[0]; 
        } 
#else 
        int  c; 
        char *s = src; 
        while ((c = s[0]) != 0) { 
         hash1 = ((hash1 << 5) + hash1)^c; 
         c = s[1]; 
         if (c == 0) 
          break; 
         hash2 = ((hash2 << 5) + hash2)^c; 
         s += 2; 
        } 
#endif 
#if DEBUG 
        // We want to ensure we can change our hash function daily. 
        // This is perfectly fine as long as you don't persist the 
        // value from GetHashCode to disk or count on String A 
        // hashing before string B. Those are bugs in your code. 
        hash1 ^= ThisAssembly.DailyBuildNumber; 
#endif 
        return hash1 + (hash2 * 1566083941); 
       } 
      } 
     } 

這是不能跨越.NET版本穩定,從散落在string.cs源代碼註釋,甚至有可能不會是穩定整個應用程序域之內相同的過程。

如果您想要一個真正穩定的哈希代碼,可以「安全地」保存在AppDomain之外,請查看System.Security.Cryptography中的哈希函數。 MD5是低安全工作可接受的,SHAx口味更好。

True Hashes只有一種方法,它不可能真正反轉散列,因爲它是一個「有損」的過程。如果開發人員從索賠中獲得代碼,他們可以反轉哈希,但他們要麼撒謊,弄錯或者沒有實施正確的哈希。

+0

非常感謝發佈這個。我完全同意哈希的可逆性,從一開始,這種說法對我來說看起來很奇怪。我想也許這是.NET中的一些實現功能。 –

+0

如果你想要一個真正的,穩定的哈希碼,可以「安全地」保存在AppDomain之外,請查看'System.Security.Cryptograph'中的哈希函數。對於低安全性的工作,MD5是可以接受的,而SHAx的風格則更好。 –

+0

我不是在.NET中編寫代碼,我只是移植已經寫入的服務,但感謝您的提示。 –

1

to Bradley's answer上添加這是一個穩定的哈希碼,基於64位實現的String.GetHashCode(),它不使用我之前寫過的不安全代碼。

public static class StringExtensionMethods 
{ 
    public static int GetStableHashCode(this string str) 
    { 
     unchecked 
     { 
      int hash1 = 5381; 
      int hash2 = hash1; 

      for(int i = 0; i < str.Length && str[i] != '\0'; i += 2) 
      { 
       hash1 = ((hash1 << 5) + hash1)^str[i]; 
       if (i == str.Length - 1 || str[i+1] == '\0') 
        break; 
       hash2 = ((hash2 << 5) + hash2)^str[i+1]; 
      } 

      return hash1 + (hash2*1566083941); 
     } 
    } 
} 
+0

謝謝!實際上,這看起來要簡單得多。 –

+0

@SlavaFominII刷新,我只是修復了一些我剛纔注意到的錯誤,使它更精確地匹配內置的GetHashCode函數。 –

+0

我的端口不正確或算法不同。這給了我值'2d58dd33de6c6c00',但原始值是'5FCF2D5'。 –