From 9f638d74401bfa2eb0c0e94f215db5a3ec4ab959 Mon Sep 17 00:00:00 2001 From: Marcus Denker Date: Tue, 22 Oct 2024 15:13:52 +0200 Subject: [PATCH] - add #testRemovePage for BTree - move some methods up from SkipList --- src/Soil-Core-Tests/SoilBTreeTest.class.st | 24 +++++++++++++++ src/Soil-Core/SoilBTreeHeaderPage.class.st | 6 ++++ src/Soil-Core/SoilBasicBTree.class.st | 30 +++++++++++++++++++ src/Soil-Core/SoilFreePage.class.st | 5 ++++ src/Soil-Core/SoilIndex.class.st | 35 ++++++++++++++++++++++ src/Soil-Core/SoilSkipList.class.st | 35 ---------------------- 6 files changed, 100 insertions(+), 35 deletions(-) diff --git a/src/Soil-Core-Tests/SoilBTreeTest.class.st b/src/Soil-Core-Tests/SoilBTreeTest.class.st index 69b5d9dd..6a5ab6c0 100644 --- a/src/Soil-Core-Tests/SoilBTreeTest.class.st +++ b/src/Soil-Core-Tests/SoilBTreeTest.class.st @@ -500,6 +500,30 @@ SoilBTreeTest >> testRemoveKey [ self should: [ index removeKey: 20 ] raise: KeyNotFound. ] +{ #category : #tests } +SoilBTreeTest >> testRemovePage [ + + | capacityFirst offset capacity iterator | + capacityFirst := index firstPage itemCapacity. + 1 to: capacityFirst do: [ :n | + index at: n put: (n asByteArrayOfSize: 8) ]. + self assert: index pages size equals: 3. + offset := capacityFirst. + iterator := index newIterator. + 2 to: 6 do: [ :p | + index at: offset + 1 put: ((offset + 1) asByteArrayOfSize: 8). + capacity := index lastPage itemCapacity. + offset + 2 to: (offset + capacity) do: [:n | . + index at: n put: (n asByteArrayOfSize: 8) ]. + offset := offset + capacity ]. + self assert: index pages size equals: 13. + index recyclePage: (index pages at: 3). + self assert: index pages size equals: 13. + "after, index 3 should not be a value in any index page item" + index indexPages do: [ :indexPage | self assert: (indexPage items noneSatisfy: [ :item | item value == 3 ]) ] + +] + { #category : #tests } SoilBTreeTest >> testSize [ diff --git a/src/Soil-Core/SoilBTreeHeaderPage.class.st b/src/Soil-Core/SoilBTreeHeaderPage.class.st index b6c5c5fb..29a3404f 100644 --- a/src/Soil-Core/SoilBTreeHeaderPage.class.st +++ b/src/Soil-Core/SoilBTreeHeaderPage.class.st @@ -27,6 +27,12 @@ SoilBTreeHeaderPage >> decreaseSize [ needWrite := true ] +{ #category : #accessing } +SoilBTreeHeaderPage >> firstFreePageIndex [ + + ^ firstFreePageIndex +] + { #category : #accessing } SoilBTreeHeaderPage >> firstFreePageIndex: anObject [ diff --git a/src/Soil-Core/SoilBasicBTree.class.st b/src/Soil-Core/SoilBasicBTree.class.st index 4a7ab4a4..64b67468 100644 --- a/src/Soil-Core/SoilBasicBTree.class.st +++ b/src/Soil-Core/SoilBasicBTree.class.st @@ -119,6 +119,16 @@ SoilBasicBTree >> pageClass [ ^ SoilBTreeDataPage ] +{ #category : #removing } +SoilBasicBTree >> recyclePage: aPage [ + "the header stays untouched even it it gets empty" + aPage isHeaderPage ifTrue: [ ^ aPage ]. + "remove page from chain of item pages" + self removePage: aPage. + "add page to free list chain" + ^ self addToFreePages: aPage +] + { #category : #removing } SoilBasicBTree >> removeKey: key ifAbsent: aBlock [ | removedItem | @@ -127,6 +137,26 @@ SoilBasicBTree >> removeKey: key ifAbsent: aBlock [ ^removedItem value ] +{ #category : #removing } +SoilBasicBTree >> removePage: aPage [ + | iterator previousPage | + (aPage offset > 1) ifFalse: [ ^ aPage ]. + + "We use the iterator to find the previousPage, to change it's next pointer to my next" + iterator := self newIterator. + previousPage := iterator findPreviousPageOf: aPage. + previousPage next: aPage next. + + "now remove all keys, this will update the index pages" + + aPage items reverseDo: [ :item | item ifNotNil: [self removeKey: item key] ]. + + "finally remove the page from the store" + store removePageAt: aPage offset. + ^ aPage + +] + { #category : #accessing } SoilBasicBTree >> rootPage [ ^ self store pageAt: 2 diff --git a/src/Soil-Core/SoilFreePage.class.st b/src/Soil-Core/SoilFreePage.class.st index 6339c5ce..c4bb8487 100644 --- a/src/Soil-Core/SoilFreePage.class.st +++ b/src/Soil-Core/SoilFreePage.class.st @@ -51,6 +51,11 @@ SoilFreePage >> isDirty [ ^ needWrite ] +{ #category : #testing } +SoilFreePage >> isIndexPage [ + ^ false +] + { #category : #accessing } SoilFreePage >> label [ ^ 'free page' diff --git a/src/Soil-Core/SoilIndex.class.st b/src/Soil-Core/SoilIndex.class.st index e173e2e0..f236298d 100644 --- a/src/Soil-Core/SoilIndex.class.st +++ b/src/Soil-Core/SoilIndex.class.st @@ -26,6 +26,32 @@ SoilIndex >> addDirtyPage: aPage [ ] +{ #category : #adding } +SoilIndex >> addToFreePages: aPage [ + | currentFreePage newFreePage | + newFreePage := aPage asSoilFreePage. + store pageAt: aPage offset put: newFreePage. + currentFreePage := self firstFreePage + ifNil: [ + "if there is no first free page yet make aPage the + first free page" + self headerPage firstFreePageIndex: newFreePage offset. + ^ newFreePage ]. + "if there is a first free page start scanning from here for a + free page that has room to store another free page" + [ currentFreePage hasRoom ] whileFalse: [ + "if there is no room for the page to store and the current + free page does not have a next pointer we make aPage the + next" + currentFreePage next isZero + ifTrue: [ + currentFreePage next: newFreePage offset. + ^ newFreePage ] + ifFalse: [ currentFreePage := store pageAt: currentFreePage next ]]. + currentFreePage addPage: newFreePage. + ^ newFreePage +] + { #category : #private } SoilIndex >> at: key [ ^ self @@ -91,6 +117,15 @@ SoilIndex >> first: anInteger [ ^ self newIterator first: anInteger ] +{ #category : #accessing } +SoilIndex >> firstFreePage [ + | index | + index := self headerPage firstFreePageIndex. + ^ index isZero + ifTrue: [ ^ nil ] + ifFalse: [ store pageAt: index ] +] + { #category : #accessing } SoilIndex >> firstPage [ ^ self headerPage diff --git a/src/Soil-Core/SoilSkipList.class.st b/src/Soil-Core/SoilSkipList.class.st index 2a290225..e1448809 100644 --- a/src/Soil-Core/SoilSkipList.class.st +++ b/src/Soil-Core/SoilSkipList.class.st @@ -14,32 +14,6 @@ SoilSkipList >> acceptSoil: aSoilVisitor [ ^ aSoilVisitor visitSkipList: self ] -{ #category : #adding } -SoilSkipList >> addToFreePages: aPage [ - | currentFreePage newFreePage | - newFreePage := aPage asSoilFreePage. - store pageAt: aPage offset put: newFreePage. - currentFreePage := self firstFreePage - ifNil: [ - "if there is no first free page yet make aPage the - first free page" - self headerPage firstFreePageIndex: newFreePage offset. - ^ newFreePage ]. - "if there is a first free page start scanning from here for a - free page that has room to store another free page" - [ currentFreePage hasRoom ] whileFalse: [ - "if there is no room for the page to store and the current - free page does not have a next pointer we make aPage the - next" - currentFreePage next isZero - ifTrue: [ - currentFreePage next: newFreePage offset. - ^ newFreePage ] - ifFalse: [ currentFreePage := store pageAt: currentFreePage next ]]. - currentFreePage addPage: newFreePage. - ^ newFreePage -] - { #category : #converting } SoilSkipList >> asCopyOnWrite [ ^ SoilCopyOnWriteSkipList new @@ -64,15 +38,6 @@ SoilSkipList >> destroy [ path ensureDelete ] -{ #category : #accessing } -SoilSkipList >> firstFreePage [ - | index | - index := self headerPage firstFreePageIndex. - ^ index isZero - ifTrue: [ ^ nil ] - ifFalse: [ store pageAt: index ] -] - { #category : #deleting } SoilSkipList >> flush [ self store flush