/// <summary> /// Computes the fuzzy hash of the first len bytes of the buffer. /// </summary> public static string HashBuffer(byte[] buf, int len, FuzzyHashMode flags = FuzzyHashMode.None) { var ctx = new Hasher(); ctx.Update(buf, len); return(ctx.Digest(flags)); }
/// <summary> /// Obtain the fuzzy hash from the state. /// This operation does not change the state at all. It reports the hash for the /// concatenation of the data previously fed using fuzzy_update. /// </summary> private string Digest(FuzzyHashMode flags) { var result = new byte[FuzzyConstants.MaxResultLength]; var pos = 0; uint bi = _bhstart; uint h = _roll.Sum(); int i; // Exclude terminating '\0'. /* Initial blocksize guess. */ while ((uint)(((uint)FuzzyConstants.MinBlocksize) << (int)(bi)) * FuzzyConstants.SpamSumLength < _totalSize) { ++bi; if (bi >= FuzzyConstants.NumBlockhashes) { throw new OverflowException("EOVERFLOW"); } } /* Adapt blocksize guess to actual digest length. */ while (bi >= _bhend) { --bi; } while (bi > _bhstart && _bh[bi].DLen < FuzzyConstants.SpamSumLength / 2) { --bi; } var actualBlockSize = (((uint)FuzzyConstants.MinBlocksize) << (int)(bi)); var blockSizeChars = actualBlockSize.ToString().ToCharArray(); i = blockSizeChars.Length; for (int j = 0; j < i; j++) { result[j + pos] = (byte)blockSizeChars[j]; } result[i++] = (byte)':'; pos += i; i = (int)_bh[bi].DLen; if (flags.HasFlag(FuzzyHashMode.EliminateSequences)) { i = MemcpyEliminateSequences(result, pos, _bh[bi].Digest, i); } else { Array.Copy(_bh[bi].Digest, 0, result, pos, i); } pos += i; if (h != 0) { var base64Val = FuzzyConstants.Base64[_bh[bi].H % 64]; result[pos] = base64Val; if (!flags.HasFlag(FuzzyHashMode.EliminateSequences) || i < 3 || base64Val != result[pos - 1] || base64Val != result[pos - 2] || base64Val != result[pos - 3]) { ++pos; } } else if (_bh[bi].Digest[i] != '\0') { var digestVal = _bh[bi].Digest[i]; result[pos] = digestVal; if (!flags.HasFlag(FuzzyHashMode.EliminateSequences) || i < 3 || digestVal != result[pos - 1] || digestVal != result[pos - 2] || digestVal != result[pos - 3]) { ++pos; } } result[pos++] = (byte)':'; if (bi < _bhend - 1) { ++bi; i = (int)_bh[bi].DLen; if (!flags.HasFlag(FuzzyHashMode.DoNotTruncate) && i > FuzzyConstants.SpamSumLength / 2 - 1) { i = FuzzyConstants.SpamSumLength / 2 - 1; } if (flags.HasFlag(FuzzyHashMode.EliminateSequences)) { i = MemcpyEliminateSequences(result, pos, _bh[bi].Digest, i); } else { Array.Copy(_bh[bi].Digest, 0, result, pos, i); } pos += i; if (h != 0) { h = flags.HasFlag(FuzzyHashMode.DoNotTruncate) ? _bh[bi].H : _bh[bi].HalfH; var base64Val = FuzzyConstants.Base64[h % 64]; result[pos] = base64Val; if (!flags.HasFlag(FuzzyHashMode.EliminateSequences) || i < 3 || base64Val != result[pos - 1] || base64Val != result[pos - 2] || base64Val != result[pos - 3]) { ++pos; } } else { i = flags.HasFlag(FuzzyHashMode.DoNotTruncate) ? _bh[bi].Digest[_bh[bi].DLen] : _bh[bi].HalfDigest; if (i != '\0') { result[pos] = (byte)i; if (!flags.HasFlag(FuzzyHashMode.EliminateSequences) || i < 3 || i != result[pos - 1] || i != result[pos - 2] || i != result[pos - 3]) { ++pos; } } } } else if (h != 0) { result[pos++] = FuzzyConstants.Base64[_bh[bi].H % 64]; /* No need to bother with FuzzyHashMode.EliminateSequences, because this * digest has length 1. */ } return(Encoding.ASCII.GetString(result, 0, pos)); }