!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: identkeyvaluedict.gs,v 1.7 2008-01-09 22:50:11 stever Exp $
!
! Superclass Hierarchy:
!   IdentityKeyValueDictionary, KeyValueDictionary, AbstractDictionary,
!   Collection, Object.
!
!=========================================================================

! 5.0: IdentityKeyValueDictionary created in bom.c

! remove existing behavior from IdentityKeyValueDictionary
removeallmethods IdentityKeyValueDictionary
removeallclassmethods IdentityKeyValueDictionary

category: 'For Documentation Installation only'
classmethod: IdentityKeyValueDictionary
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'An IdentityKeyValueDictionary is a KeyValueDictionary that is an identity-based
 collection instead of equality-based.  That is, two keys or two values are
 considered to be the same only if they are identical; equivalent objects are
 not the same.  Thus, if you add two key-value pairs to an
 IdentityKeyValueDictionary and the keys are equivalent but not identical, then
 the result is that you have two pairs in the dictionary because the keys are
 not the same.

 IdentityKeyValueDictionary exhibits better performance than KeyValueDictionary
 and is to be preferred where it is appropriate.

 For multiuser applications that involve a lot of concurrent use of
 dictionaries, use RcKeyValueDictionary.' .
doc documentClassWith: txt.

self description: doc.
%

! ------------------- Class methods for IdentityKeyValueDictionary
! ------------------- Instance methods for IdentityKeyValueDictionary
category: 'Hashing'
method: IdentityKeyValueDictionary
hashFunction: aKey

"The hash function should perform some operation on the value of the key aKey
 which returns a value in the range 1..tableSize."

^(aKey identityHash \\ tableSize) + 1
%

! remove redundant method at:otherwise, fix bug 8188

category: 'Private'
method: IdentityKeyValueDictionary
collisionBucketClass

"Returns the class of object to create when keys collide."

^ IdentityCollisionBucket
%

category: 'Private'
method: IdentityKeyValueDictionary
compareKey: key1 with: key2

"Returns true if key1 is identical to key2; returns false otherwise."

^ key1 == key2 
%

category: 'Initializing'
method: IdentityKeyValueDictionary
initialize: itsSize

"Initializes the instance variables of the receiver to be an empty
 IdentityKeyValueDictionary of the specified size."

| theSize |
theSize := itsSize .
(theSize <= 0)
  ifTrue: [theSize _error: #rtErrArgNotPositive .  theSize := 1 ].
numElements := 0.
numCollisions := 0.
self collisionLimit: theSize  * 500 . 
  "This is where it differs from KeyValueDictionary. By setting the default
   collision limit to be high in comparison to the size of the dictionary, 
   the dictionary will only be rebuilt when the collision buckets have 
   become big."
self _basicSize: 0.  "Reset size to 0 to nil any entries"
self _basicSize: theSize * 2.
tableSize := theSize.
^self
%

! added reimplementation of rebuildTable: in gemstone64 v1.1 
category: 'Hashing'
method: IdentityKeyValueDictionary
rebuildTable: newSize

super rebuildTable: newSize .
self collisionLimit: newSize * 500 . "differs from KeyValueDictionary"
%

category: 'Canonical Symbol Support'
method: IdentityKeyValueDictionary
_selectiveAbort

""

| aKey collisionBkt |

1 to: tableSize * 2 by: 2 do: [ :tableIndex |
  nil == (aKey := self _at: tableIndex) 
    ifTrue: [ nil == (collisionBkt := self _at: (tableIndex + 1)) 
      ifFalse: [collisionBkt _selectiveAbort]].
  ].
  
super _selectiveAbort
%

category: 'Comparing'
method: IdentityKeyValueDictionary
hash

"Returns a numeric hash key for the receiver."

| hashValue |

hashValue := 97633 bitXor: (self size).
"For large dictionaries, the hash value is just a function of its size"
(self size > 64) ifTrue: [ ^ hashValue abs ].
self keysDo: [ :aKey |
   "Skip if the key is a dictionary."
   (aKey isKindOf: AbstractDictionary)
     ifFalse: [
       hashValue := hashValue bitXor: aKey identityHash
       ]
     ].
^ hashValue abs
%

! Globals at: #ProcessorScheduler put:nil is now done in bom.c

category: 'Private'
method: IdentityKeyValueDictionary
_when: aGsSocket signal: anObject
  "Used by ProcessorScheduler."
  | oldval |
  anObject _setupReapSignalLocation: self.
  oldval := self
    at: aGsSocket
    ifAbsent: 
      [self
         at: aGsSocket
         put: anObject].
  (oldval ~~ anObject) ifTrue:
    [ | newval |
      (oldval isMemberOf: Array)
        ifTrue: [newval := oldval]
        ifFalse: 
          [newval := Array new: 2.
           newval 
             at: 1
             put: oldval.].
      newval add: anObject.
      self 
        at: aGsSocket
        put: newval.].
%

category: 'Private'
method: IdentityKeyValueDictionary
_isWhenSet: aGsSocket signal: anObject
  "Used by ProcessorScheduler.
   Returns true if event is still enabled.
   Returns false if event was never set or has happened and been automatically
   unset."

  | val |

  val := self
    at: aGsSocket
    ifAbsent: [^false].
  (val == anObject)
    ifTrue: [^true]
    ifFalse: [(val isMemberOf: Array) ifTrue: [^val includesIdentical: anObject]].
  ^false
%
category: 'Private'
method: IdentityKeyValueDictionary
_cancelWhen: aGsSocket signal: anObject
  "Used by ProcessorScheduler.
   Returns true if event has already been cancelled or reaped.
   Returns false if event is cancelled by this invocation."

  | v |

  v := self
    at: aGsSocket
    ifAbsent: [^true].
  (anObject == v)
    ifTrue:
      [self removeKey: aGsSocket.
       ^false]
    ifFalse:
      [(v isMemberOf: Array)
        ifTrue:
          [v
             removeIdentical: anObject
             ifAbsent: [^true].
           v isEmpty ifTrue: [self removeKey: aGsSocket].
           ^false]].
  ^true
%

category: 'Private'
method: IdentityKeyValueDictionary
_removeValues: anObject
  "Removes every entry whose value is anObject."

  | hits |
  hits := Array new.
  self keysAndValuesDo: 
    [ :k :v |
      ((anObject == v) _or: [(v isMemberOf: Array) _and:
                             [(v includesIdentical: anObject)]])
        ifTrue: [hits add: k]].
  hits do: [ :k | self _cancelWhen: k signal: anObject].
%

category: 'Private'
method: IdentityKeyValueDictionary
_reapEventFor: aGsSocket
  | objToNotify |

  objToNotify := self
    removeKey: aGsSocket
    ifAbsent: [^false].
  (objToNotify isMemberOf: Array)
    ifTrue: [objToNotify do: [:o | (o ~~ nil) ifTrue: [o _reapSignal: aGsSocket]]]
    ifFalse: [objToNotify _reapSignal: aGsSocket].
  ^true
%

category: 'Private'
method: IdentityKeyValueDictionary
_unscheduleProcess: aGsProcess
  "Used by ProcessorScheduler"

  (self == ProcessorScheduler scheduler _suspendedDict)
    ifTrue: 
      [ | oldLocation |
        oldLocation := self
          removeKey: aGsProcess
          ifAbsent: [nil].
        (oldLocation ~~ nil) ifTrue: [oldLocation _unscheduleProcess: aGsProcess].]
    ifFalse: [self _removeValues: aGsProcess].
%

category: 'Private'
method: IdentityKeyValueDictionary
_changePriority: aGsProcess from: oldPriority
  "Used by GsProcess to change the priority of a GsProcess in the receiver."
  "nothing needs to be done"
%


