Unwind just the "pseudorandom probing" part of recent sets,tables changes (#13816)
* Unwind just the "pseudorandom probing" (whole hash-code-keyed variable stride double hashing) part of recent sets & tables changes (which has still been causing bugs over a month later (e.g., two days ago https://github.com/nim-lang/Nim/issues/13794) as well as still having several "figure this out" implementation question comments in them (see just diffs of this PR). This topic has been discussed in many places: https://github.com/nim-lang/Nim/issues/13393 https://github.com/nim-lang/Nim/pull/13418 https://github.com/nim-lang/Nim/pull/13440 https://github.com/nim-lang/Nim/issues/13794 Alternative/non-mandatory stronger integer hashes (or vice-versa opt-in identity hashes) are a better solution that is more general (no illusion of one hard-coded sequence solving all problems) while retaining the virtues of linear probing such as cache obliviousness and age-less tables under delete-heavy workloads (still untested after a month of this change). The only real solution for truly adversarial keys is a hash keyed off of data unobservable to attackers. That all fits better with a few families of user-pluggable/define-switchable hashes which can be provided in a separate PR more about `hashes.nim`. This PR carefully preserves the better (but still hard coded!) probing of the `intsets` and other recent fixes like `move` annotations, hash order invariant tests, `intsets.missingOrExcl` fixing, and the move of `rightSize` into `hashcommon.nim`. * Fix `data.len` -> `dataLen` problem.
This commit is contained in:
parent
7abeba6aeb
commit
b1aa3b1eea
8 changed files with 99 additions and 175 deletions
|
|
@ -35,11 +35,10 @@ proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
|
|||
|
||||
proc enlarge[A](s: var HashSet[A]) =
|
||||
var n: KeyValuePairSeq[A]
|
||||
newSeq(n, s.counter.rightSize)
|
||||
newSeq(n, len(s.data) * growthFactor)
|
||||
swap(s.data, n) # n is now old seq
|
||||
s.countDeleted = 0
|
||||
for i in countup(0, high(n)):
|
||||
if isFilledAndValid(n[i].hcode):
|
||||
if isFilled(n[i].hcode):
|
||||
var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
|
||||
rawInsert(s, s.data, n[i].key, n[i].hcode, j)
|
||||
|
||||
|
|
@ -69,6 +68,11 @@ template containsOrInclImpl() {.dirty.} =
|
|||
rawInsert(s, s.data, key, hc, -1 - index)
|
||||
inc(s.counter)
|
||||
|
||||
template doWhile(a, b) =
|
||||
while true:
|
||||
b
|
||||
if not a: break
|
||||
|
||||
proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} =
|
||||
var hc: Hash
|
||||
var i = rawGet(s, key, hc)
|
||||
|
|
@ -78,9 +82,17 @@ proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} =
|
|||
if i >= 0:
|
||||
result = false
|
||||
dec(s.counter)
|
||||
inc(s.countDeleted)
|
||||
s.data[i].hcode = deletedMarker
|
||||
s.data[i].key = default(type(s.data[i].key))
|
||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||
var r = j # though may be adaptable to other simple sequences.
|
||||
s.data[i].hcode = 0 # mark current EMPTY
|
||||
s.data[i].key = default(type(s.data[i].key))
|
||||
doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
||||
i = (i + 1) and msk # increment mod table size
|
||||
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
||||
return
|
||||
r = s.data[i].hcode and msk # "home" location of key@i
|
||||
s.data[j] = move(s.data[i]) # data[i] will be marked EMPTY next loop
|
||||
|
||||
template dollarImpl() {.dirty.} =
|
||||
result = "{"
|
||||
|
|
@ -113,7 +125,7 @@ proc enlarge[A](s: var OrderedSet[A]) =
|
|||
swap(s.data, n)
|
||||
while h >= 0:
|
||||
var nxt = n[h].next
|
||||
if isFilled(n[h].hcode): # should be isFilledAndValid once tombstones are used
|
||||
if isFilled(n[h].hcode):
|
||||
var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
|
||||
rawInsert(s, s.data, n[h].key, n[h].hcode, j)
|
||||
h = nxt
|
||||
|
|
@ -131,7 +143,7 @@ proc exclImpl[A](s: var OrderedSet[A], key: A): bool {.inline.} =
|
|||
result = true
|
||||
while h >= 0:
|
||||
var nxt = n[h].next
|
||||
if isFilled(n[h].hcode): # should be isFilledAndValid once tombstones are used
|
||||
if isFilled(n[h].hcode):
|
||||
if n[h].hcode == hc and n[h].key == key:
|
||||
dec s.counter
|
||||
result = false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue