/// <summary> /// Calculates the hash for a requested span. /// This will try to use a cached hash if the data was already accessed before, to avoid re-hashing. /// </summary> /// <param name="data">Data to be hashed</param> /// <returns>Hash of the data</returns> private uint CalcHashCached(ReadOnlySpan <byte> data) { HashState state = default; bool found = false; for (int i = _cachedHashes.Count - 1; i >= 0; i--) { int cachedHashSize = _cachedHashes.Keys[i]; if (cachedHashSize < data.Length) { state = _cachedHashes.Values[i]; found = true; break; } } if (!found) { state = new HashState(); state.Initialize(); } state.Continue(data); _cachedHashes[data.Length & ~7] = state; return(state.Finalize(data)); }
/// <summary> /// One shot hash calculation for a given data. /// </summary> /// <param name="data">Data to be hashed</param> /// <returns>Hash of the given data</returns> public static uint CalcHash(ReadOnlySpan <byte> data) { HashState state = new HashState(); state.Initialize(); state.Continue(data); return(state.Finalize(data)); }
/// <summary> /// Gets an existing item from the table, or adds a new one if not present. /// </summary> /// <param name="data">Data</param> /// <param name="item">Item associated with the data</param> /// <returns>Existing item, or <paramref name="item"/> if not present</returns> public T GetOrAdd(byte[] data, T item) { SizeEntry sizeEntry; int index = BinarySearch(_sizeTable, data.Length); if (index < _sizeTable.Count && _sizeTable[index].Size == data.Length) { sizeEntry = _sizeTable[index]; } else { if (index < _sizeTable.Count && _sizeTable[index].Size < data.Length) { index++; } sizeEntry = new SizeEntry(data.Length); _sizeTable.Insert(index, sizeEntry); for (int i = index + 1; i < _sizeTable.Count; i++) { _sizeTable[i].FillPartials(sizeEntry); } } HashState hashState = new HashState(); hashState.Initialize(); for (int i = 0; i < index; i++) { ReadOnlySpan <byte> dataSlice = new ReadOnlySpan <byte>(data).Slice(0, _sizeTable[i].Size); hashState.Continue(dataSlice); _sizeTable[i].AddPartial(data, hashState.Finalize(dataSlice)); } hashState.Continue(data); return(sizeEntry.GetOrAdd(data, hashState.Finalize(data), item)); }
/// <summary> /// Adds a partial entry to the hash table. /// </summary> /// <param name="ownerData">Full data</param> /// <param name="ownSize">Size of the sub-region of <paramref name="ownerData"/> used by the partial entry</param> /// <returns>True if added, false otherwise</returns> public bool AddPartial(byte[] ownerData, int ownSize) { ReadOnlySpan <byte> data = new ReadOnlySpan <byte>(ownerData).Slice(0, ownSize); return(AddPartial(ownerData, HashState.CalcHash(data), ownSize)); }