!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: identsoftcollisionbucket.gs,v 1.6 2008-01-09 22:50:11 stever Exp $
!
! Superclass Hierarchy:
! Object
!   Collection
!     SequenceableCollection
!       Array
!         AbstractCollisionBucket( numElements)
!           CollisionBucket( keyValueDictionary)
!             IdentityCollisionBucket
!	        IdentitySoftCollisionBucket
!=========================================================================

expectvalue %String
run
| res cls oldCls clsName |
clsName := #IdentitySoftCollisionBucket .
oldCls := Globals at:clsName otherwise: nil .
oldCls == nil ifTrue:[
  res := IdentityCollisionBucket _newKernelSubclass: clsName"IdentitySoftCollisionBucket"
    instVarNames: #( #lastCleanupCount )
    classVars: #()
    classInstVars: #()
    poolDictionaries: #[]
    inDictionary: Globals
    constraints: #[ #[ 'lastCleanupCount', SmallInteger ]]
    instancesInvariant: false
    isModifiable: true
    reservedOop: 947 .
  cls := Globals at: clsName .
  cls makeInstancesNonPersistent .
  cls _disallowGciCreateStore .
  cls immediateInvariant .
] ifFalse: [
  res := clsName asString + 'already exists'.
  oldCls instancesNonPersistent ifFalse:[ nil error:'should be NP' ].
].
^ res
%


! Remove existing behavior 
removeallmethods IdentitySoftCollisionBucket
removeallclassmethods 

category: 'For Documentation Installation only'
classmethod
installDocumentation

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

txt := (GsDocText new) details:
'A IdentitySoftCollisionBucket is an IdentityCollisionBucket intended to
 store key/SoftReference pairs .
 key/SoftReference pairs whose SoftReferences have been cleared by 
 the in-memory garbage collector will be removed from a 
 IdentitySoftCollisionBucket during the next at:put: operation 
 following a garbage collection which cleared the references.  
 
 Cleared SoftReferences will also be removed when their key matches
 the argument key for a lookup operation .  The lookup code will
 not remove cleared SoftReferences for non-matching keys.

 Instances of IdentitySoftCollisionBucket are non-persistent and may
 not be committed to the repository.' .
doc documentClassWith: txt.

txt := (GsDocText new) details:
'Private to the implementation of IdentitySoftCollisionBucket, used to manage
 removal of SoftReferences.'.
doc documentInstVar: #lastCleanupCount with: txt.

self description: doc.
%

category: 'Instance creation'
classmethod
new

^ super new _initializeCleanup
%


category: 'Private'
method
_initializeCleanup

  lastCleanupCount := 0 
%

category: 'Private'
method
_referenceAt: idx 

" for the key stored at offset idx ,
  return the SofReference if softRef.value non-nil, otherwise
  remove the soft reference and associated key .
  Does not increment the use count of softRef .
" 
| aSoftRef val | 
aSoftRef := self _at: idx + 1 .
aSoftRef ~~ nil ifTrue:[ 
  val := aSoftRef _value .
  val == nil ifTrue:[ 
    self removeFrom: idx to: idx + 1 . "remove key and SoftRef" 
    numElements := numElements - 1 .
    numElements <= 0 ifTrue:[
      numElements < 0 ifTrue:[ nil error:'underflow' ].
      keyValueDictionary _removedElements: 1 collisions: 0
    ] ifFalse:[ 
      keyValueDictionary _removedElements: 1 collisions: 1
    ].
    ^ nil .
  ] .
  ^ aSoftRef
].
^ nil
%

category: 'Accessing'
method
referenceAt: aKey ifAbsent: aBlock

"Returns the non-cleared SoftReference that corresponds to aKey.  
 If no such key/SoftRef pair exists,
 returns the result of evaluating the zero-argument block aBlock."

  | index keyIndex aSoftRef |
  keyIndex := self searchForKey: aKey.
  keyIndex ~~ nil ifTrue: [ 
    index := (keyIndex * 2) - 1 .
    aSoftRef:=  self _referenceAt: index .
    aSoftRef == nil ifTrue:[ ^ self _reportKeyNotFound: aKey with: aBlock ] .
    ^ aSoftRef 
  ].
  ^ self _reportKeyNotFound: aKey with: aBlock 
%
 
category: 'Accessing'
method
referenceAt: aKey otherwise: aValue

"Returns the non-cleared SoftReference that corresponds to aKey.
 If no such key/SoftRef pair exists, returns aValue ."

  | keyIndex index aSoftRef |
  keyIndex := self searchForKey: aKey.
  keyIndex ~~ nil ifTrue:[
    index := (keyIndex * 2) - 1 .
    aSoftRef :=  self _referenceAt: index .
    aSoftRef == nil ifTrue:[ ^ aValue ].
    ^ aSoftRef 
  ].
  ^ aValue   
%

! inherit from AbstractCollisionBucket,   at: anIndex putKey: aKey
! inherit from AbstractCollisionBucket,  at: anIndex putValue: aValue

category: 'Updating'
method
at: aKey put: aValue  

"disallowed, should use  at:put:keyValDict_coll:  "

self shouldNotImplement: #at:put:
%

category: 'Updating'
method
at: aKey put: aValue keyValDict_coll: aKeyValDict

"Stores the aKey/aValue pair in the receiver.  
 Returns 1 if this at:put: added a new key, 0 if this at:put:
 replaced a SoftReference for an existing  key .

 aValue is expected to be a SoftReference.

 Also removes key/SoftReference pairs whose SoftReference has been
 cleared by the garbage collector."

self cleanupReferences .
^ super at: aKey put: aValue keyValDict_coll: aKeyValDict
%

category: 'Removing'
method
removeKey: aKey ifAbsent: aBlock

"Removes the key/value pair having the key aKey.  If aKey is not found,
 returns the result of evaluating the zero-argument block aBlock.

 Also removes key/SoftReference pairs whose SoftReference has been
 cleared by the garbage collector."

self cleanupReferences .
^ super removeKey: aKey ifAbsent: aBlock 
%

! added for 36675
category: 'Removing'
method
removeKey: aKey otherwise: notFoundValue

"Removes the key/value pair having the key aKey.  If aKey is not found,
 returns the notFoundValue .

 Also removes key/SoftReference pairs whose SoftReference has been
 cleared by the garbage collector."

self cleanupReferences .
^ super removeKey: aKey otherwise: notFoundValue
%

category: 'Private'
method
_markSweepsThatClearedSoftRefsCount

"Returns OM.markSweepsThatClearedSoftRefsCount as a positive SmallInteger"

<primitive: 553>

self _primitiveFailed: #_markSweepsThatClearedSoftRefsCount
%

category: 'Cleanup'
method
cleanupReferences

| currCount |
currCount := self _markSweepsThatClearedSoftRefsCount .
currCount = lastCleanupCount ifFalse:[ | lastKeyIdx startNumElem |
  startNumElem := numElements .
  lastKeyIdx := (numElements * 2) - 1 .
  lastKeyIdx downTo: 1 by: 2 do:[ :idx | | aRef aVal |
    aRef := self _at: idx + 1.
    aVal := aRef _value  .
    aVal == nil ifTrue:[
      self removeFrom: idx  to: idx+ 1 . "remove key/softRef pair"
      numElements := numElements - 1 .
    ]. 
  ]. 
  lastCleanupCount := currCount .
  numElements == startNumElem ifFalse:[ 
    keyValueDictionary _bucketSizeChangeFrom:startNumElem to: numElements .
  ].
].
%


