From aa5d009c6b9a2a0dc4fc77e5d53086df971c8e2c Mon Sep 17 00:00:00 2001 From: Alokzh Date: Thu, 12 Jun 2025 23:32:52 +0530 Subject: [PATCH 1/6] Refactored CTBuffer to implement both FIFO & LIFO Variations of Buffer --- .../CTBufferTest.class.st | 153 +++--------------- .../CTAbstractBuffer.class.st | 109 +++++++++++++ src/Containers-Buffer/CTBuffer.class.st | 137 ---------------- src/Containers-Buffer/CTFIFOBuffer.class.st | 9 ++ src/Containers-Buffer/CTLIFOBuffer.class.st | 10 ++ 5 files changed, 154 insertions(+), 264 deletions(-) create mode 100644 src/Containers-Buffer/CTAbstractBuffer.class.st delete mode 100644 src/Containers-Buffer/CTBuffer.class.st create mode 100644 src/Containers-Buffer/CTFIFOBuffer.class.st create mode 100644 src/Containers-Buffer/CTLIFOBuffer.class.st diff --git a/src/Containers-Buffer-Tests/CTBufferTest.class.st b/src/Containers-Buffer-Tests/CTBufferTest.class.st index 61e959f..c3c98a2 100644 --- a/src/Containers-Buffer-Tests/CTBufferTest.class.st +++ b/src/Containers-Buffer-Tests/CTBufferTest.class.st @@ -5,7 +5,8 @@ Class { #name : 'CTBufferTest', #superclass : 'TestCase', #instVars : [ - 'buffer' + 'fifoBuffer', + 'lifoBuffer' ], #category : 'Containers-Buffer-Tests', #package : 'Containers-Buffer-Tests' @@ -13,143 +14,41 @@ Class { { #category : 'running' } CTBufferTest >> setUp [ - - super setUp. - buffer := CTBuffer new -] - -{ #category : 'tests' } -CTBufferTest >> testAvailableSize [ - - buffer := CTBuffer withCapacity: 10. - buffer - put: 4; - put: 5. - - self assert: buffer availableSpace equals: 8 -] - -{ #category : 'tests' } -CTBufferTest >> testAvailableSizeWhenExcedeedCapacity [ - - buffer := CTBuffer withCapacity: 4. - - 1 to: 10 do: [ :i | buffer put: i ]. - self assert: buffer availableSpace equals: 0. - - buffer pop. - self assert: buffer availableSpace equals: 1. - - buffer pop. - self assert: buffer availableSpace equals: 2 +super setUp. + fifoBuffer := CTFIFOBuffer withCapacity: 3. + lifoBuffer := CTLIFOBuffer withCapacity: 3 ] { #category : 'tests' } -CTBufferTest >> testEmptyBuffer [ +CTBufferTest >> testBufferCreationWithCapacity [ - self assert: buffer isEmpty. - self should: [ buffer get ] raise: Error -] - -{ #category : 'tests' } -CTBufferTest >> testIndeces [ - - buffer := CTBuffer withCapacity: 4. - self assert: buffer readIndex equals: 1. - self assert: buffer writeIndex equals: 1. - - buffer put: 1. - self assert: buffer readIndex equals: 1. - self assert: buffer writeIndex equals: 2. - buffer put: 1. - self assert: buffer readIndex equals: 1. - self assert: buffer writeIndex equals: 3. + | buffer | + buffer := CTFIFOBuffer withCapacity: 5. - buffer put: 1. - self assert: buffer readIndex equals: 1. - self assert: buffer writeIndex equals: 4. - - buffer put: 1. - self assert: buffer readIndex equals: 1. - self assert: buffer writeIndex equals: 1. - - buffer put: 1. - self assert: buffer readIndex equals: 2. - self assert: buffer writeIndex equals: 2. - - buffer pop. - self assert: buffer readIndex equals: 3. - self assert: buffer writeIndex equals: 2 -] - -{ #category : 'tests' } -CTBufferTest >> testPop [ - - self assert: buffer isEmpty. - buffer put: 'test'. - self deny: buffer isEmpty. - - self assert: buffer pop equals: 'test'. - self assert: buffer isEmpty -] - -{ #category : 'tests' } -CTBufferTest >> testPut [ - - buffer put: 'first'. - self deny: buffer isEmpty. - - buffer pop. - self assert: buffer isEmpty. - - buffer put: 'second'. - self deny: buffer isEmpty + self assert: buffer capacity equals: 5. + self assert: buffer size equals: 0. + self assert: buffer isEmpty. + self deny: buffer isFull. + self assert: buffer availableSpace equals: 5 ] { #category : 'tests' } -CTBufferTest >> testPutAll [ - - buffer := CTBuffer withCapacity: 4. - buffer putAll: (1 to: 6). - - self assert: buffer size equals: 4. - - self assertCollection: buffer elements hasSameElements: #( 5 6 3 4 ). - - self assert: buffer pop equals: 3. - self assert: buffer pop equals: 4. - self assert: buffer pop equals: 5. - self assert: buffer pop equals: 6. - - self assert: buffer isEmpty -] - -{ #category : 'tests' } -CTBufferTest >> testPutWhenSizeExcedeesCapacity [ - - buffer := CTBuffer withCapacity: 4. - 1 to: 6 do: [ :i | buffer put: i ]. - - self assert: buffer size equals: 4. - - self assertCollection: buffer elements hasSameElements: #( 5 6 3 4 ). - - self assert: buffer pop equals: 3. - self assert: buffer pop equals: 4. - self assert: buffer pop equals: 5. - self assert: buffer pop equals: 6. - - self assert: buffer isEmpty +CTBufferTest >> testBufferCreationWithInvalidCapacity [ + + self should: [ CTFIFOBuffer withCapacity: 0 ] raise: Error. + self should: [ CTFIFOBuffer withCapacity: -1 ] raise: Error. + self should: [ CTLIFOBuffer withCapacity: 0 ] raise: Error ] { #category : 'tests' } -CTBufferTest >> testTop [ +CTBufferTest >> testDefaultBufferCreation [ - buffer - put: 4; - put: 5. - - self assert: buffer top equals: 4. - self assert: buffer size equals: 2 + | buffer | + buffer := CTFIFOBuffer new. + + self assert: buffer capacity equals: 10. + self assert: buffer isEmpty. + self assert: buffer readIndex equals: 1. + self assert: buffer writeIndex equals: 1 ] diff --git a/src/Containers-Buffer/CTAbstractBuffer.class.st b/src/Containers-Buffer/CTAbstractBuffer.class.st new file mode 100644 index 0000000..f731637 --- /dev/null +++ b/src/Containers-Buffer/CTAbstractBuffer.class.st @@ -0,0 +1,109 @@ +" +I am an abstract circular buffer with fixed capacity. I provide common functionality for both FIFO and LIFO circular buffers. My subclasses implement specific ordering behaviors by overriding updateReadIndex. +" +Class { + #name : 'CTAbstractBuffer', + #superclass : 'Object', + #instVars : [ + 'elements', + 'readIndex', + 'writeIndex', + 'currentSize', + 'capacity' + ], + #category : 'Containers-Buffer', + #package : 'Containers-Buffer' +} + +{ #category : 'instance creation' } +CTAbstractBuffer class >> withCapacity: anInteger [ + + anInteger < 1 ifTrue: [ self error: 'Capacity must be positive' ]. + ^ self new + capacity: anInteger; + yourself +] + +{ #category : 'accessing' } +CTAbstractBuffer >> availableSpace [ + + "Return the number of additional elements that can be stored" + + ^ capacity - currentSize +] + +{ #category : 'accessing' } +CTAbstractBuffer >> capacity [ + + "Return the maximum capacity of the buffer" + + ^ capacity +] + +{ #category : 'accessing' } +CTAbstractBuffer >> capacity: anInteger [ + + "Set the capacity and initialize elements array" + + capacity := anInteger. + elements := Array new: capacity +] + +{ #category : 'accessing' } +CTAbstractBuffer >> elements [ + + ^ elements +] + +{ #category : 'initialization' } +CTAbstractBuffer >> initialize [ + + super initialize. + capacity := 10. + elements := Array new: capacity. + readIndex := 1. + writeIndex := 1. + currentSize := 0 +] + +{ #category : 'testing' } +CTAbstractBuffer >> isEmpty [ + + "Return true if buffer has no elements" + + ^ currentSize = 0 +] + +{ #category : 'testing' } +CTAbstractBuffer >> isFull [ + + "Return true if buffer is at maximum capacity" + + ^ currentSize = capacity + +] + +{ #category : 'accessing' } +CTAbstractBuffer >> readIndex [ + + "Return the current read index" + + ^ readIndex + +] + +{ #category : 'accessing' } +CTAbstractBuffer >> size [ + + "Return the current number of elements in the buffer" + + ^ currentSize +] + +{ #category : 'accessing' } +CTAbstractBuffer >> writeIndex [ + + "Return the current write index" + + ^ writeIndex +] diff --git a/src/Containers-Buffer/CTBuffer.class.st b/src/Containers-Buffer/CTBuffer.class.st deleted file mode 100644 index 1545642..0000000 --- a/src/Containers-Buffer/CTBuffer.class.st +++ /dev/null @@ -1,137 +0,0 @@ -" -I represent a circular buffer with fixed capacity. - -A Buffer provides efficient temporary storage with automatic wraparound when full. It supports O(1) insertion and removal operations, making it ideal for streaming data, producer-consumer scenarios, and situations requiring fixed-memory buffering. -" -Class { - #name : 'CTBuffer', - #superclass : 'Object', - #instVars : [ - 'elements', - 'readIndex', - 'writeIndex', - 'currentSize', - 'capacity' - ], - #category : 'Containers-Buffer', - #package : 'Containers-Buffer' -} - -{ #category : 'instance creation' } -CTBuffer class >> withCapacity: anInteger [ - - anInteger < 1 ifTrue: [ self error: 'Capacity must be not negative' ]. - - ^ self new - capacity: anInteger; - yourself -] - -{ #category : 'accessing' } -CTBuffer >> availableSpace [ - "Return the number of additional elements that can be stored" - - ^ capacity - currentSize -] - -{ #category : 'accessing' } -CTBuffer >> capacity [ - - "Return the maximum capacity of the buffer" - - ^ capacity -] - -{ #category : 'accessing' } -CTBuffer >> capacity: anInteger [ - - capacity := anInteger. - elements := Array new: capacity -] - -{ #category : 'accessing' } -CTBuffer >> elements [ - ^ elements -] - -{ #category : 'initialization' } -CTBuffer >> initialize [ - - super initialize. - capacity := 10. - elements := Array new: capacity. - readIndex := 1. - writeIndex := 1. - currentSize := 0 -] - -{ #category : 'testing' } -CTBuffer >> isEmpty [ - - ^ currentSize = 0 -] - -{ #category : 'testing' } -CTBuffer >> isFull [ - - ^ currentSize = capacity -] - -{ #category : 'actions api' } -CTBuffer >> pop [ - - "Remove and return the oldest element" - - | element | - self isEmpty ifTrue: [ self error: 'Buffer is empty' ]. - - element := elements at: readIndex. - elements at: readIndex put: nil. - readIndex := readIndex \\ capacity + 1. - currentSize := currentSize - 1. - ^ element -] - -{ #category : 'actions api' } -CTBuffer >> put: anObject [ - - "Add an element to the buffer. If full, overwrites oldest element" - - elements at: writeIndex put: anObject. - writeIndex := writeIndex \\ capacity + 1. - - self isFull - ifTrue: [ readIndex := readIndex \\ capacity + 1 ] - ifFalse: [ currentSize := currentSize + 1 ]. - - ^ anObject -] - -{ #category : 'actions api' } -CTBuffer >> putAll: aCollection [ - - aCollection do: [ :e | self put: e ]. - ^ aCollection last -] - -{ #category : 'accessing' } -CTBuffer >> readIndex [ - ^ readIndex -] - -{ #category : 'accessing' } -CTBuffer >> size [ - - ^ currentSize -] - -{ #category : 'actions api' } -CTBuffer >> top [ - - ^ elements at: readIndex -] - -{ #category : 'accessing' } -CTBuffer >> writeIndex [ - ^ writeIndex -] diff --git a/src/Containers-Buffer/CTFIFOBuffer.class.st b/src/Containers-Buffer/CTFIFOBuffer.class.st new file mode 100644 index 0000000..91767f8 --- /dev/null +++ b/src/Containers-Buffer/CTFIFOBuffer.class.st @@ -0,0 +1,9 @@ +" +I am a FIFO circular buffer with fixed capacity. I provide first-in, first-out ordering for elements. +" +Class { + #name : 'CTFIFOBuffer', + #superclass : 'CTAbstractBuffer', + #category : 'Containers-Buffer', + #package : 'Containers-Buffer' +} diff --git a/src/Containers-Buffer/CTLIFOBuffer.class.st b/src/Containers-Buffer/CTLIFOBuffer.class.st new file mode 100644 index 0000000..9fa6890 --- /dev/null +++ b/src/Containers-Buffer/CTLIFOBuffer.class.st @@ -0,0 +1,10 @@ +" +I am a LIFO circular buffer with fixed capacity. +I provide last-in, first-out ordering for elements. +" +Class { + #name : 'CTLIFOBuffer', + #superclass : 'CTAbstractBuffer', + #category : 'Containers-Buffer', + #package : 'Containers-Buffer' +} From 2d2c551d176ab6a3adaa0e73fff9acae6b100b38 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 13 Jun 2025 07:31:44 +0530 Subject: [PATCH 2/6] Added Core Operations for FIFO & LIFO Buffers with Tests --- .../CTBufferTest.class.st | 123 ++++++++++++++++++ .../CTAbstractBuffer.class.st | 98 ++++++++++++++ src/Containers-Buffer/CTFIFOBuffer.class.st | 25 ++++ src/Containers-Buffer/CTLIFOBuffer.class.st | 26 ++++ 4 files changed, 272 insertions(+) diff --git a/src/Containers-Buffer-Tests/CTBufferTest.class.st b/src/Containers-Buffer-Tests/CTBufferTest.class.st index c3c98a2..6b54a1b 100644 --- a/src/Containers-Buffer-Tests/CTBufferTest.class.st +++ b/src/Containers-Buffer-Tests/CTBufferTest.class.st @@ -41,6 +41,19 @@ CTBufferTest >> testBufferCreationWithInvalidCapacity [ self should: [ CTLIFOBuffer withCapacity: 0 ] raise: Error ] +{ #category : 'tests' } +CTBufferTest >> testClearBuffer [ + + fifoBuffer push: 'a'; push: 'b'; push: 'c'. + fifoBuffer clear. + + self assert: fifoBuffer isEmpty. + self assert: fifoBuffer size equals: 0. + self assert: fifoBuffer readIndex equals: 1. + self assert: fifoBuffer writeIndex equals: 1. + self assert: fifoBuffer availableSpace equals: 3 +] + { #category : 'tests' } CTBufferTest >> testDefaultBufferCreation [ @@ -52,3 +65,113 @@ CTBufferTest >> testDefaultBufferCreation [ self assert: buffer readIndex equals: 1. self assert: buffer writeIndex equals: 1 ] + +{ #category : 'tests' } +CTBufferTest >> testEmptyBufferErrors [ + + + self should: [ fifoBuffer peek ] raise: Error. + self should: [ fifoBuffer pop ] raise: Error. + self should: [ lifoBuffer peek ] raise: Error. + self should: [ lifoBuffer pop ] raise: Error +] + +{ #category : 'tests' } +CTBufferTest >> testMultipleOverwrites [ + + "FIFO: Maintains oldest-to-newest order" + fifoBuffer push: 'a'; push: 'b'; push: 'c'. + fifoBuffer push: 'd'; push: 'e'; push: 'f'. + self assert: fifoBuffer size equals: 3. + self assert: fifoBuffer pop equals: 'd'. + self assert: fifoBuffer pop equals: 'e'. + self assert: fifoBuffer pop equals: 'f'. + self assert: fifoBuffer isEmpty. + + "LIFO: Maintains newest-to-oldest order" + lifoBuffer push: 'a'; push: 'b'; push: 'c'. + lifoBuffer push: 'd'; push: 'e'; push: 'f'. + self assert: lifoBuffer size equals: 3. + self assert: lifoBuffer pop equals: 'f'. + self assert: lifoBuffer pop equals: 'e'. + self assert: lifoBuffer pop equals: 'd'. + self assert: lifoBuffer isEmpty +] + +{ #category : 'tests' } +CTBufferTest >> testOverwriteBehavior [ + + "FIFO: Overwrites oldest, readIndex points to next oldest" + fifoBuffer push: 'first'; push: 'second'; push: 'third'. + fifoBuffer push: 'fourth'. + self assert: fifoBuffer size equals: 3. + self assert: fifoBuffer isFull. + self assert: fifoBuffer peek equals: 'second'. "First was overwritten" + self assert: fifoBuffer readIndex equals: 2. + self assert: fifoBuffer writeIndex equals: 2. + + "LIFO: Overwrites oldest, readIndex points to newest" + lifoBuffer push: 'first'; push: 'second'; push: 'third'. + lifoBuffer push: 'fourth'. + self assert: lifoBuffer size equals: 3. + self assert: lifoBuffer isFull. + self assert: lifoBuffer peek equals: 'fourth'. "Newest element" + self assert: lifoBuffer readIndex equals: 1. + self assert: lifoBuffer writeIndex equals: 2 +] + +{ #category : 'tests' } +CTBufferTest >> testPeekAndPop [ + + "FIFO: Oldest First" + fifoBuffer push: 'first'; push: 'second'; push: 'third'. + self assert: fifoBuffer peek equals: 'first'. + self assert: fifoBuffer size equals: 3. + self assert: fifoBuffer pop equals: 'first'. + self assert: fifoBuffer size equals: 2. + self assert: fifoBuffer peek equals: 'second'. + + "LIFO: Newest First" + lifoBuffer push: 'first'; push: 'second'; push: 'third'. + self assert: lifoBuffer peek equals: 'third'. + self assert: lifoBuffer size equals: 3. + self assert: lifoBuffer pop equals: 'third'. + self assert: lifoBuffer size equals: 2. + self assert: lifoBuffer peek equals: 'second' +] + +{ #category : 'tests' } +CTBufferTest >> testPushAllCollection [ + + | result | + result := fifoBuffer pushAll: #('a' 'b' 'c'). + + self assert: fifoBuffer size equals: 3. + self assert: result equals: 'c'. "Returns last element" + self assert: fifoBuffer isFull +] + +{ #category : 'tests' } +CTBufferTest >> testPushMultipleElements [ + + fifoBuffer push: 'first'; push: 'second'; push: 'third'. + + self assert: fifoBuffer size equals: 3. + self assert: fifoBuffer isFull. + self assert: fifoBuffer availableSpace equals: 0. + self assert: fifoBuffer writeIndex equals: 1. + self assert: fifoBuffer readIndex equals: 1 +] + +{ #category : 'tests' } +CTBufferTest >> testPushSingleElement [ + + fifoBuffer push: 'first'. + + self assert: fifoBuffer size equals: 1. + self deny: fifoBuffer isEmpty. + self deny: fifoBuffer isFull. + self assert: fifoBuffer availableSpace equals: 2. + self assert: fifoBuffer writeIndex equals: 2. + self assert: fifoBuffer readIndex equals: 1 +] diff --git a/src/Containers-Buffer/CTAbstractBuffer.class.st b/src/Containers-Buffer/CTAbstractBuffer.class.st index f731637..62006c2 100644 --- a/src/Containers-Buffer/CTAbstractBuffer.class.st +++ b/src/Containers-Buffer/CTAbstractBuffer.class.st @@ -49,6 +49,17 @@ CTAbstractBuffer >> capacity: anInteger [ elements := Array new: capacity ] +{ #category : 'actions' } +CTAbstractBuffer >> clear [ + + "Remove all elements from the buffer" + + 1 to: capacity do: [ :i | elements at: i put: nil ]. + readIndex := 1. + writeIndex := 1. + currentSize := 0 +] + { #category : 'accessing' } CTAbstractBuffer >> elements [ @@ -83,6 +94,69 @@ CTAbstractBuffer >> isFull [ ] +{ #category : 'accessing' } +CTAbstractBuffer >> peek [ + + "Return next element to be retrieved without removing it" + + self isEmpty ifTrue: [ self error: 'Buffer is empty' ]. + ^ elements at: readIndex +] + +{ #category : 'removing' } +CTAbstractBuffer >> pop [ + + "Remove and return element from readIndex position" + + | element | + self isEmpty ifTrue: [ self error: 'Buffer is empty' ]. + + element := elements at: readIndex. + elements at: readIndex put: nil. + self updateReadIndex. + currentSize := currentSize - 1. + ^ element +] + +{ #category : 'testing' } +CTAbstractBuffer >> push: anObject [ + + "Add an element to the buffer" + + elements at: writeIndex put: anObject. + + currentSize = 0 + ifTrue: [ + "Very first element - both FIFO and LIFO point readIndex to it" + readIndex := writeIndex. + currentSize := 1. + ] + ifFalse: [ + self isFull + ifTrue: [ + "Buffer is full - we just overwrote an element" + self updateReadIndexForOverwrite. + ] + ifFalse: [ + "Normal case - adding to non-full buffer" + self updateReadIndexForAdd. + currentSize := currentSize + 1. + ]. + ]. + + writeIndex := writeIndex \\ capacity + 1. + ^ anObject +] + +{ #category : 'testing' } +CTAbstractBuffer >> pushAll: aCollection [ + + "Add all elements from aCollection to the buffer" + + aCollection do: [ :e | self push: e ]. + ^ aCollection isEmpty ifFalse: [ aCollection last ] +] + { #category : 'accessing' } CTAbstractBuffer >> readIndex [ @@ -100,6 +174,30 @@ CTAbstractBuffer >> size [ ^ currentSize ] +{ #category : 'private' } +CTAbstractBuffer >> updateReadIndex [ + + "Update readIndex after pop or overwrite - subclass responsibility" + + self subclassResponsibility +] + +{ #category : 'private' } +CTAbstractBuffer >> updateReadIndexForAdd [ + + "Update readIndex when adding new element - subclass responsibility" + + self subclassResponsibility +] + +{ #category : 'private' } +CTAbstractBuffer >> updateReadIndexForOverwrite [ + + "Update readIndex when overwriting an element - subclass responsibility" + + self subclassResponsibility +] + { #category : 'accessing' } CTAbstractBuffer >> writeIndex [ diff --git a/src/Containers-Buffer/CTFIFOBuffer.class.st b/src/Containers-Buffer/CTFIFOBuffer.class.st index 91767f8..78417c0 100644 --- a/src/Containers-Buffer/CTFIFOBuffer.class.st +++ b/src/Containers-Buffer/CTFIFOBuffer.class.st @@ -7,3 +7,28 @@ Class { #category : 'Containers-Buffer', #package : 'Containers-Buffer' } + +{ #category : 'private' } +CTFIFOBuffer >> updateReadIndex [ + + "In FIFO, readIndex moves to next oldest element after pop" + + readIndex := readIndex \\ capacity + 1 +] + +{ #category : 'private' } +CTFIFOBuffer >> updateReadIndexForAdd [ + + "In FIFO, readIndex stays pointing to oldest element when adding" + + "No change needed - readIndex keeps pointing to oldest" +] + +{ #category : 'private' } +CTFIFOBuffer >> updateReadIndexForOverwrite [ + + "In FIFO, when overwriting, readIndex moves to next oldest element" + + readIndex := readIndex \\ capacity + 1 + +] diff --git a/src/Containers-Buffer/CTLIFOBuffer.class.st b/src/Containers-Buffer/CTLIFOBuffer.class.st index 9fa6890..b87ff29 100644 --- a/src/Containers-Buffer/CTLIFOBuffer.class.st +++ b/src/Containers-Buffer/CTLIFOBuffer.class.st @@ -8,3 +8,29 @@ Class { #category : 'Containers-Buffer', #package : 'Containers-Buffer' } + +{ #category : 'private' } +CTLIFOBuffer >> updateReadIndex [ + + "In LIFO, readIndex moves to next newest element after pop" + + readIndex := readIndex = 1 + ifTrue: [ capacity ] + ifFalse: [ readIndex - 1 ] +] + +{ #category : 'private' } +CTLIFOBuffer >> updateReadIndexForAdd [ + + "In LIFO, readIndex moves to the newly added element" + + readIndex := writeIndex +] + +{ #category : 'private' } +CTLIFOBuffer >> updateReadIndexForOverwrite [ + + "In LIFO, readIndex points to the newly added element (newest)" + + readIndex := writeIndex +] From 6a911de282b567496d8adebf26f04000b34020e3 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 13 Jun 2025 21:17:51 +0530 Subject: [PATCH 3/6] Complete remaining methods & add remaining tests --- .../CTBufferTest.class.st | 468 ++++++++++++++++++ .../CTAbstractBuffer.class.st | 40 +- src/Containers-Buffer/CTFIFOBuffer.class.st | 15 + src/Containers-Buffer/CTLIFOBuffer.class.st | 17 + 4 files changed, 523 insertions(+), 17 deletions(-) diff --git a/src/Containers-Buffer-Tests/CTBufferTest.class.st b/src/Containers-Buffer-Tests/CTBufferTest.class.st index 6b54a1b..d81bf88 100644 --- a/src/Containers-Buffer-Tests/CTBufferTest.class.st +++ b/src/Containers-Buffer-Tests/CTBufferTest.class.st @@ -19,6 +19,73 @@ super setUp. lifoBuffer := CTLIFOBuffer withCapacity: 3 ] +{ #category : 'tests' } +CTBufferTest >> testAvailableSpace [ + + self assert: fifoBuffer availableSpace equals: 3. + self assert: lifoBuffer availableSpace equals: 3. + + fifoBuffer push: 'a'. + lifoBuffer push: 'x'. + self assert: fifoBuffer availableSpace equals: 2. + self assert: lifoBuffer availableSpace equals: 2. + + fifoBuffer + push: 'b'; + push: 'c'. + lifoBuffer + push: 'y'; + push: 'z'. + self assert: fifoBuffer availableSpace equals: 0. + self assert: lifoBuffer availableSpace equals: 0. + + fifoBuffer pop. + lifoBuffer pop. + self assert: fifoBuffer availableSpace equals: 1. + self assert: lifoBuffer availableSpace equals: 1 +] + +{ #category : 'tests' } +CTBufferTest >> testAvailableSpaceTracking [ + + self assert: fifoBuffer availableSpace equals: 3. + fifoBuffer push: 'a'. + self assert: fifoBuffer availableSpace equals: 2. + fifoBuffer push: 'b'; push: 'c'. + self assert: fifoBuffer availableSpace equals: 0. + fifoBuffer pop. + self assert: fifoBuffer availableSpace equals: 1 +] + +{ #category : 'tests' } +CTBufferTest >> testBrowserHistory [ + + | browserHistory visitedPages | + browserHistory := CTLIFOBuffer withCapacity: 3. + visitedPages := OrderedCollection new. + + browserHistory push: 'https://google.com'. + browserHistory push: 'https://github.com/pharo-containers'. + browserHistory push: 'https://stackoverflow.com'. + browserHistory push: 'https://reddit.com'. + + visitedPages add: browserHistory pop. + self assert: visitedPages last equals: 'https://reddit.com'. + + browserHistory push: 'https://pharo.org'. + + [ browserHistory isEmpty ] whileFalse: [ + visitedPages add: browserHistory pop ]. + + self assert: visitedPages size equals: 4. + self assert: visitedPages first equals: 'https://reddit.com'. + self assert: (visitedPages at: 2) equals: 'https://pharo.org'. + self assert: (visitedPages at: 3) equals: 'https://stackoverflow.com'. + self + assert: visitedPages last + equals: 'https://github.com/pharo-containers' +] + { #category : 'tests' } CTBufferTest >> testBufferCreationWithCapacity [ @@ -41,6 +108,61 @@ CTBufferTest >> testBufferCreationWithInvalidCapacity [ self should: [ CTLIFOBuffer withCapacity: 0 ] raise: Error ] +{ #category : 'tests' } +CTBufferTest >> testChatMessageQueue [ + + | chatQueue displayedMessages | + chatQueue := CTFIFOBuffer withCapacity: 4. + displayedMessages := OrderedCollection new. + + chatQueue push: 'Alok: Hello everyone!'. + chatQueue push: 'Sebastian: Hey Alok!'. + chatQueue push: 'Gordana: How is everyone doing?'. + chatQueue push: 'Sebastian: Great to see you all!'. + + displayedMessages add: chatQueue pop. + self assert: displayedMessages last equals: 'Alok: Hello everyone!'. + + chatQueue push: 'Alok: Sorry I was late!'. + + [ chatQueue isEmpty ] whileFalse: [ + displayedMessages add: chatQueue pop ]. + + self assert: displayedMessages size equals: 5. + self assert: (displayedMessages at: 2) equals: 'Sebastian: Hey Alok!'. + self assert: displayedMessages last equals: 'Alok: Sorry I was late!' +] + +{ #category : 'tests' } +CTBufferTest >> testClearAfterOverwrite [ + + fifoBuffer + push: 'a'; + push: 'b'; + push: 'c'; + push: 'd'. + lifoBuffer + push: 'x'; + push: 'y'; + push: 'z'; + push: 'w'. + + fifoBuffer clear. + lifoBuffer clear. + + self assert: fifoBuffer isEmpty. + self assert: lifoBuffer isEmpty. + self assert: fifoBuffer size equals: 0. + self assert: lifoBuffer size equals: 0. + self assert: fifoBuffer availableSpace equals: 3. + self assert: lifoBuffer availableSpace equals: 3. + + fifoBuffer push: 'new'. + lifoBuffer push: 'new'. + self assert: fifoBuffer peek equals: 'new'. + self assert: lifoBuffer peek equals: 'new' +] + { #category : 'tests' } CTBufferTest >> testClearBuffer [ @@ -54,6 +176,27 @@ CTBufferTest >> testClearBuffer [ self assert: fifoBuffer availableSpace equals: 3 ] +{ #category : 'tests' } +CTBufferTest >> testCopyBuffer [ + + | copy | + fifoBuffer push: 'a'; push: 'b'; push: 'c'. + copy := fifoBuffer copy. + + self assert: copy class equals: CTFIFOBuffer. + self assert: copy capacity equals: fifoBuffer capacity. + self assert: copy size equals: fifoBuffer size. + self deny: copy identicalTo: fifoBuffer. + + "Test that copy has same contents and order" + self assert: copy pop equals: 'a'. + self assert: copy pop equals: 'b'. + self assert: copy pop equals: 'c'. + + "Original should be unchanged" + self assert: fifoBuffer size equals: 3 +] + { #category : 'tests' } CTBufferTest >> testDefaultBufferCreation [ @@ -66,6 +209,53 @@ CTBufferTest >> testDefaultBufferCreation [ self assert: buffer writeIndex equals: 1 ] +{ #category : 'tests' } +CTBufferTest >> testDoIteration [ + + | fifoElements lifoElements | + "FIFO: Iterates oldest to newest" + fifoBuffer + push: 'first'; + push: 'second'; + push: 'third'. + fifoElements := OrderedCollection new. + fifoBuffer do: [ :each | fifoElements add: each ]. + self + assert: fifoElements asArray + equals: #( 'first' 'second' 'third' ). + + "LIFO: Iterates newest to oldest" + lifoBuffer + push: 'first'; + push: 'second'; + push: 'third'. + lifoElements := OrderedCollection new. + lifoBuffer do: [ :each | lifoElements add: each ]. + self + assert: lifoElements asArray + equals: #( 'third' 'second' 'first' ) +] + +{ #category : 'tests' } +CTBufferTest >> testDoIterationAfterOverwrite [ + + | fifoElements lifoElements | + + "FIFO: After Overwrite" + fifoBuffer push: 'a'; push: 'b'; push: 'c'. + fifoBuffer push: 'd'. "Overwrites 'a'" + fifoElements := OrderedCollection new. + fifoBuffer do: [ :each | fifoElements add: each ]. + self assert: fifoElements asArray equals: #('b' 'c' 'd'). + + "LIFO: After Overwrite" + lifoBuffer push: 'a'; push: 'b'; push: 'c'. + lifoBuffer push: 'd'. "Overwrites 'a'" + lifoElements := OrderedCollection new. + lifoBuffer do: [ :each | lifoElements add: each ]. + self assert: lifoElements asArray equals: #('d' 'c' 'b') +] + { #category : 'tests' } CTBufferTest >> testEmptyBufferErrors [ @@ -76,6 +266,99 @@ CTBufferTest >> testEmptyBufferErrors [ self should: [ lifoBuffer pop ] raise: Error ] +{ #category : 'tests' } +CTBufferTest >> testIndicesAfterClear [ + + fifoBuffer + push: 'a'; + push: 'b'; + push: 'c'. + lifoBuffer + push: 'x'; + push: 'y'; + push: 'z'. + + fifoBuffer pop. + lifoBuffer pop. + + fifoBuffer clear. + lifoBuffer clear. + + self assert: fifoBuffer readIndex equals: 1. + self assert: fifoBuffer writeIndex equals: 1. + self assert: lifoBuffer readIndex equals: 1. + self assert: lifoBuffer writeIndex equals: 1 +] + +{ #category : 'tests' } +CTBufferTest >> testIsEmpty [ + + self assert: fifoBuffer isEmpty. + self assert: lifoBuffer isEmpty. + + fifoBuffer push: 'test'. + lifoBuffer push: 'test'. + self deny: fifoBuffer isEmpty. + self deny: lifoBuffer isEmpty. + + fifoBuffer pop. + lifoBuffer pop. + self assert: fifoBuffer isEmpty. + self assert: lifoBuffer isEmpty +] + +{ #category : 'tests' } +CTBufferTest >> testIsFull [ + + self deny: fifoBuffer isFull. + self deny: lifoBuffer isFull. + + fifoBuffer + push: 'a'; + push: 'b'; + push: 'c'. + lifoBuffer + push: 'x'; + push: 'y'; + push: 'z'. + + self assert: fifoBuffer isFull. + self assert: lifoBuffer isFull. + + fifoBuffer pop. + lifoBuffer pop. + self deny: fifoBuffer isFull. + self deny: lifoBuffer isFull +] + +{ #category : 'tests' } +CTBufferTest >> testMixedPushPopOperations [ + + fifoBuffer + push: 'a'; + push: 'b'. + self assert: fifoBuffer pop equals: 'a'. + fifoBuffer + push: 'c'; + push: 'd'. + self assert: fifoBuffer pop equals: 'b'. + self assert: fifoBuffer pop equals: 'c'. + self assert: fifoBuffer pop equals: 'd'. + self assert: fifoBuffer isEmpty. + + lifoBuffer + push: 'x'; + push: 'y'. + self assert: lifoBuffer pop equals: 'y'. + lifoBuffer + push: 'z'; + push: 'w'. + self assert: lifoBuffer pop equals: 'w'. + self assert: lifoBuffer pop equals: 'z'. + self assert: lifoBuffer pop equals: 'x'. + self assert: lifoBuffer isEmpty +] + { #category : 'tests' } CTBufferTest >> testMultipleOverwrites [ @@ -120,6 +403,30 @@ CTBufferTest >> testOverwriteBehavior [ self assert: lifoBuffer writeIndex equals: 2 ] +{ #category : 'tests' } +CTBufferTest >> testOverwriteBehaviorWithAvailableSpace [ + + fifoBuffer + push: 'a'; + push: 'b'; + push: 'c'. + lifoBuffer + push: 'x'; + push: 'y'; + push: 'z'. + + self assert: fifoBuffer availableSpace equals: 0. + self assert: lifoBuffer availableSpace equals: 0. + + fifoBuffer push: 'd'. + lifoBuffer push: 'w'. + + self assert: fifoBuffer availableSpace equals: 0. + self assert: lifoBuffer availableSpace equals: 0. + self assert: fifoBuffer size equals: 3. + self assert: lifoBuffer size equals: 3 +] + { #category : 'tests' } CTBufferTest >> testPeekAndPop [ @@ -140,6 +447,32 @@ CTBufferTest >> testPeekAndPop [ self assert: lifoBuffer peek equals: 'second' ] +{ #category : 'tests' } +CTBufferTest >> testPopAndPushSequence [ + + fifoBuffer + push: 'a'; + push: 'b'. + fifoBuffer pop. + fifoBuffer push: 'c'. + + self assert: fifoBuffer size equals: 2. + self assert: fifoBuffer pop equals: 'b'. + self assert: fifoBuffer pop equals: 'c'. + self assert: fifoBuffer isEmpty. + + lifoBuffer + push: 'x'; + push: 'y'. + lifoBuffer pop. + lifoBuffer push: 'z'. + + self assert: lifoBuffer size equals: 2. + self assert: lifoBuffer pop equals: 'z'. + self assert: lifoBuffer pop equals: 'x'. + self assert: lifoBuffer isEmpty +] + { #category : 'tests' } CTBufferTest >> testPushAllCollection [ @@ -175,3 +508,138 @@ CTBufferTest >> testPushSingleElement [ self assert: fifoBuffer writeIndex equals: 2. self assert: fifoBuffer readIndex equals: 1 ] + +{ #category : 'tests' } +CTBufferTest >> testReadIndexAfterPop [ + + "FIFO: readIndex moves to next oldest" + fifoBuffer push: 'a'; push: 'b'; push: 'c'. + fifoBuffer pop. + self assert: fifoBuffer readIndex equals: 2. + + "LIFO: readIndex moves to next newest" + lifoBuffer push: 'x'; push: 'y'; push: 'z'. + lifoBuffer pop. + self assert: lifoBuffer readIndex equals: 2 +] + +{ #category : 'tests' } +CTBufferTest >> testReadIndexBehaviorOnAdd [ + + "FIFO: readIndex stays on oldest" + fifoBuffer push: 'first'. + self assert: fifoBuffer readIndex equals: 1. + fifoBuffer push: 'second'. + self assert: fifoBuffer readIndex equals: 1. + fifoBuffer push: 'third'. + self assert: fifoBuffer readIndex equals: 1. + + "LIFO: readIndex moves to newest" + lifoBuffer push: 'first'. + self assert: lifoBuffer readIndex equals: 1. + lifoBuffer push: 'second'. + self assert: lifoBuffer readIndex equals: 2. + lifoBuffer push: 'third'. + self assert: lifoBuffer readIndex equals: 3 +] + +{ #category : 'tests' } +CTBufferTest >> testReadIndexBehaviorOnOverwrite [ + "FIFO: readIndex moves away from overwritten element" + + fifoBuffer + push: 'a'; + push: 'b'; + push: 'c'. + self assert: fifoBuffer readIndex equals: 1. + fifoBuffer push: 'd'. + self assert: fifoBuffer readIndex equals: 2. + fifoBuffer push: 'e'. + self assert: fifoBuffer readIndex equals: 3. + + "LIFO: readIndex points to newest element" + lifoBuffer + push: 'a'; + push: 'b'; + push: 'c'. + self assert: lifoBuffer readIndex equals: 3. + lifoBuffer push: 'd'. + self assert: lifoBuffer readIndex equals: 1. + lifoBuffer push: 'e'. + self assert: lifoBuffer readIndex equals: 2 +] + +{ #category : 'tests' } +CTBufferTest >> testSize [ + + self assert: fifoBuffer size equals: 0. + self assert: lifoBuffer size equals: 0. + + fifoBuffer push: 'a'. + lifoBuffer push: 'x'. + self assert: fifoBuffer size equals: 1. + self assert: lifoBuffer size equals: 1. + + fifoBuffer + push: 'b'; + push: 'c'. + lifoBuffer + push: 'y'; + push: 'z'. + self assert: fifoBuffer size equals: 3. + self assert: lifoBuffer size equals: 3. + + fifoBuffer pop. + lifoBuffer pop. + self assert: fifoBuffer size equals: 2. + self assert: lifoBuffer size equals: 2 +] + +{ #category : 'tests' } +CTBufferTest >> testWriteIndexAfterPopAndOverwrite [ + + | writeIndexAfterPop | + fifoBuffer + push: 'a'; + push: 'b'; + push: 'c'. + fifoBuffer pop. + + writeIndexAfterPop := fifoBuffer writeIndex. + + fifoBuffer push: 'd'. + fifoBuffer push: 'e'. + + self assert: fifoBuffer size equals: 3. + + lifoBuffer + push: 'x'; + push: 'y'; + push: 'z'. + lifoBuffer pop. + lifoBuffer push: 'w'. + + self assert: lifoBuffer size equals: 3. + self assert: lifoBuffer peek equals: 'w' +] + +{ #category : 'tests' } +CTBufferTest >> testWriteIndexBehavior [ + + self assert: fifoBuffer writeIndex equals: 1. + self assert: lifoBuffer writeIndex equals: 1. + + fifoBuffer push: 'a'. + lifoBuffer push: 'x'. + self assert: fifoBuffer writeIndex equals: 2. + self assert: lifoBuffer writeIndex equals: 2. + + fifoBuffer + push: 'b'; + push: 'c'. + lifoBuffer + push: 'y'; + push: 'z'. + self assert: fifoBuffer writeIndex equals: 1. + self assert: lifoBuffer writeIndex equals: 1 +] diff --git a/src/Containers-Buffer/CTAbstractBuffer.class.st b/src/Containers-Buffer/CTAbstractBuffer.class.st index 62006c2..8e12327 100644 --- a/src/Containers-Buffer/CTAbstractBuffer.class.st +++ b/src/Containers-Buffer/CTAbstractBuffer.class.st @@ -43,8 +43,6 @@ CTAbstractBuffer >> capacity [ { #category : 'accessing' } CTAbstractBuffer >> capacity: anInteger [ - "Set the capacity and initialize elements array" - capacity := anInteger. elements := Array new: capacity ] @@ -60,6 +58,23 @@ CTAbstractBuffer >> clear [ currentSize := 0 ] +{ #category : 'copying' } +CTAbstractBuffer >> copy [ + + "Return a copy of the buffer with same contents" + + | copy | + copy := self class withCapacity: capacity. + self do: [ :each | copy push: each ]. + ^ copy +] + +{ #category : 'enumerating' } +CTAbstractBuffer >> do: aBlock [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAbstractBuffer >> elements [ @@ -105,14 +120,14 @@ CTAbstractBuffer >> peek [ { #category : 'removing' } CTAbstractBuffer >> pop [ - "Remove and return element from readIndex position" - + | element | self isEmpty ifTrue: [ self error: 'Buffer is empty' ]. - + element := elements at: readIndex. elements at: readIndex put: nil. + self updateReadIndex. currentSize := currentSize - 1. ^ element @@ -120,9 +135,8 @@ CTAbstractBuffer >> pop [ { #category : 'testing' } CTAbstractBuffer >> push: anObject [ - "Add an element to the buffer" - + elements at: writeIndex put: anObject. currentSize = 0 @@ -151,26 +165,20 @@ CTAbstractBuffer >> push: anObject [ { #category : 'testing' } CTAbstractBuffer >> pushAll: aCollection [ - "Add all elements from aCollection to the buffer" - aCollection do: [ :e | self push: e ]. ^ aCollection isEmpty ifFalse: [ aCollection last ] ] { #category : 'accessing' } CTAbstractBuffer >> readIndex [ - - "Return the current read index" - ^ readIndex + ^ readIndex ] { #category : 'accessing' } CTAbstractBuffer >> size [ - "Return the current number of elements in the buffer" - ^ currentSize ] @@ -199,9 +207,7 @@ CTAbstractBuffer >> updateReadIndexForOverwrite [ ] { #category : 'accessing' } -CTAbstractBuffer >> writeIndex [ +CTAbstractBuffer >> writeIndex [ - "Return the current write index" - ^ writeIndex ] diff --git a/src/Containers-Buffer/CTFIFOBuffer.class.st b/src/Containers-Buffer/CTFIFOBuffer.class.st index 78417c0..0f589de 100644 --- a/src/Containers-Buffer/CTFIFOBuffer.class.st +++ b/src/Containers-Buffer/CTFIFOBuffer.class.st @@ -8,6 +8,21 @@ Class { #package : 'Containers-Buffer' } +{ #category : 'enumerating' } +CTFIFOBuffer >> do: aBlock [ + + "Iterate over all elements from oldest to newest" + + | index | + self isEmpty ifTrue: [ ^ self ]. + + index := readIndex. + currentSize timesRepeat: [ + aBlock value: (elements at: index). + index := index \\ capacity + 1 + ] +] + { #category : 'private' } CTFIFOBuffer >> updateReadIndex [ diff --git a/src/Containers-Buffer/CTLIFOBuffer.class.st b/src/Containers-Buffer/CTLIFOBuffer.class.st index b87ff29..7be3dc6 100644 --- a/src/Containers-Buffer/CTLIFOBuffer.class.st +++ b/src/Containers-Buffer/CTLIFOBuffer.class.st @@ -9,6 +9,23 @@ Class { #package : 'Containers-Buffer' } +{ #category : 'enumerating' } +CTLIFOBuffer >> do: aBlock [ + + "Iterate over all elements from newest to oldest" + + | index | + self isEmpty ifTrue: [ ^ self ]. + + index := readIndex. + currentSize timesRepeat: [ + aBlock value: (elements at: index). + index := index = 1 + ifTrue: [ capacity ] + ifFalse: [ index - 1 ] + ] +] + { #category : 'private' } CTLIFOBuffer >> updateReadIndex [ From 77771e56afdc89a4e6fdd0b35ab588fa6f1da421 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 13 Jun 2025 23:06:31 +0530 Subject: [PATCH 4/6] Remove redundant tests for FIFO and LIFO buffer --- .../CTBufferTest.class.st | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/src/Containers-Buffer-Tests/CTBufferTest.class.st b/src/Containers-Buffer-Tests/CTBufferTest.class.st index d81bf88..d06925e 100644 --- a/src/Containers-Buffer-Tests/CTBufferTest.class.st +++ b/src/Containers-Buffer-Tests/CTBufferTest.class.st @@ -45,18 +45,6 @@ CTBufferTest >> testAvailableSpace [ self assert: lifoBuffer availableSpace equals: 1 ] -{ #category : 'tests' } -CTBufferTest >> testAvailableSpaceTracking [ - - self assert: fifoBuffer availableSpace equals: 3. - fifoBuffer push: 'a'. - self assert: fifoBuffer availableSpace equals: 2. - fifoBuffer push: 'b'; push: 'c'. - self assert: fifoBuffer availableSpace equals: 0. - fifoBuffer pop. - self assert: fifoBuffer availableSpace equals: 1 -] - { #category : 'tests' } CTBufferTest >> testBrowserHistory [ @@ -331,56 +319,6 @@ CTBufferTest >> testIsFull [ self deny: lifoBuffer isFull ] -{ #category : 'tests' } -CTBufferTest >> testMixedPushPopOperations [ - - fifoBuffer - push: 'a'; - push: 'b'. - self assert: fifoBuffer pop equals: 'a'. - fifoBuffer - push: 'c'; - push: 'd'. - self assert: fifoBuffer pop equals: 'b'. - self assert: fifoBuffer pop equals: 'c'. - self assert: fifoBuffer pop equals: 'd'. - self assert: fifoBuffer isEmpty. - - lifoBuffer - push: 'x'; - push: 'y'. - self assert: lifoBuffer pop equals: 'y'. - lifoBuffer - push: 'z'; - push: 'w'. - self assert: lifoBuffer pop equals: 'w'. - self assert: lifoBuffer pop equals: 'z'. - self assert: lifoBuffer pop equals: 'x'. - self assert: lifoBuffer isEmpty -] - -{ #category : 'tests' } -CTBufferTest >> testMultipleOverwrites [ - - "FIFO: Maintains oldest-to-newest order" - fifoBuffer push: 'a'; push: 'b'; push: 'c'. - fifoBuffer push: 'd'; push: 'e'; push: 'f'. - self assert: fifoBuffer size equals: 3. - self assert: fifoBuffer pop equals: 'd'. - self assert: fifoBuffer pop equals: 'e'. - self assert: fifoBuffer pop equals: 'f'. - self assert: fifoBuffer isEmpty. - - "LIFO: Maintains newest-to-oldest order" - lifoBuffer push: 'a'; push: 'b'; push: 'c'. - lifoBuffer push: 'd'; push: 'e'; push: 'f'. - self assert: lifoBuffer size equals: 3. - self assert: lifoBuffer pop equals: 'f'. - self assert: lifoBuffer pop equals: 'e'. - self assert: lifoBuffer pop equals: 'd'. - self assert: lifoBuffer isEmpty -] - { #category : 'tests' } CTBufferTest >> testOverwriteBehavior [ From ee21b775a055eba1889beba6ad3990ca88531f39 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 13 Jun 2025 23:34:20 +0530 Subject: [PATCH 5/6] Added `updateWriteIndexAfterPop:` fixing failing Test's issue --- src/Containers-Buffer-Tests/CTBufferTest.class.st | 2 +- src/Containers-Buffer/CTAbstractBuffer.class.st | 14 ++++++++++++-- src/Containers-Buffer/CTFIFOBuffer.class.st | 6 ++++++ src/Containers-Buffer/CTLIFOBuffer.class.st | 7 +++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Containers-Buffer-Tests/CTBufferTest.class.st b/src/Containers-Buffer-Tests/CTBufferTest.class.st index d06925e..91338ff 100644 --- a/src/Containers-Buffer-Tests/CTBufferTest.class.st +++ b/src/Containers-Buffer-Tests/CTBufferTest.class.st @@ -1,5 +1,5 @@ " -A CTBufferTest is a test class for testing the behavior of CTBuffer +Comprehensive test suite for CTBuffer classes including CTFIFOBuffer and CTLIFOBuffer. " Class { #name : 'CTBufferTest', diff --git a/src/Containers-Buffer/CTAbstractBuffer.class.st b/src/Containers-Buffer/CTAbstractBuffer.class.st index 8e12327..2377c63 100644 --- a/src/Containers-Buffer/CTAbstractBuffer.class.st +++ b/src/Containers-Buffer/CTAbstractBuffer.class.st @@ -122,13 +122,15 @@ CTAbstractBuffer >> peek [ CTAbstractBuffer >> pop [ "Remove and return element from readIndex position" - | element | + | element poppedIndex| self isEmpty ifTrue: [ self error: 'Buffer is empty' ]. - + element := elements at: readIndex. elements at: readIndex put: nil. + poppedIndex := readIndex. self updateReadIndex. + self updateWriteIndexAfterPop: poppedIndex. currentSize := currentSize - 1. ^ element ] @@ -206,6 +208,14 @@ CTAbstractBuffer >> updateReadIndexForOverwrite [ self subclassResponsibility ] +{ #category : 'private' } +CTAbstractBuffer >> updateWriteIndexAfterPop: poppedIndex [ + + "Update writeIndex after popping an element - subclass responsibility" + + self subclassResponsibility +] + { #category : 'accessing' } CTAbstractBuffer >> writeIndex [ diff --git a/src/Containers-Buffer/CTFIFOBuffer.class.st b/src/Containers-Buffer/CTFIFOBuffer.class.st index 0f589de..4710149 100644 --- a/src/Containers-Buffer/CTFIFOBuffer.class.st +++ b/src/Containers-Buffer/CTFIFOBuffer.class.st @@ -47,3 +47,9 @@ CTFIFOBuffer >> updateReadIndexForOverwrite [ readIndex := readIndex \\ capacity + 1 ] + +{ #category : 'private' } +CTFIFOBuffer >> updateWriteIndexAfterPop: poppedIndex [ + + "Do nothing - writeIndex continues in sequence" +] diff --git a/src/Containers-Buffer/CTLIFOBuffer.class.st b/src/Containers-Buffer/CTLIFOBuffer.class.st index 7be3dc6..b9d29ec 100644 --- a/src/Containers-Buffer/CTLIFOBuffer.class.st +++ b/src/Containers-Buffer/CTLIFOBuffer.class.st @@ -51,3 +51,10 @@ CTLIFOBuffer >> updateReadIndexForOverwrite [ readIndex := writeIndex ] + +{ #category : 'private' } +CTLIFOBuffer >> updateWriteIndexAfterPop: poppedIndex [ + + "Update writeIndex after popping an element - subclass responsibility" + writeIndex := poppedIndex +] From 3a7ada53d47da0201da34e78b266b71690854f00 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 13 Jun 2025 23:46:11 +0530 Subject: [PATCH 6/6] Update README.md --- README.md | 121 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index d2c7aac..c9f3517 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Containers-Buffer -A Circular Buffer implementation providing efficient temporary storage with fixed capacity and automatic wraparound functionality. +A Circular Buffer implementation providing efficient temporary storage with fixed capacity and automatic wraparound functionality. Available in both FIFO (First In, First Out) and LIFO (Last In, First Out) variants. ![Pharo Version](https://img.shields.io/badge/Pharo-10+-blue) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) @@ -8,6 +8,30 @@ A Circular Buffer implementation providing efficient temporary storage with fixe A Buffer (also known as a circular buffer or ring buffer) is a data structure that uses a fixed-size array as if it were connected end-to-end. It provides efficient insertion and removal operations with constant time complexity O(1). When the buffer reaches its maximum capacity, new elements overwrite the oldest ones, making it perfect for streaming data and producer-consumer scenarios. +### We provide two types of buffers: +- FIFO Buffer - First In, First Out (Queue behavior) +- LIFO Buffer - Last In, First Out (Stack behavior) + +## Loading +The following script installs Containers-Buffer in Pharo. + +```smalltalk +Metacello new + baseline: 'ContainersBuffer'; + repository: 'github://pharo-containers/Containers-Buffer/src'; + load. +``` + +## If you want to depend on it + +Add the following code to your Metacello baseline or configuration + +```smalltalk +spec + baseline: 'ContainersBuffer' + with: [ spec repository: 'github://pharo-containers/Containers-Buffer/src' ]. +``` + ## Why use Containers-Buffer? Buffers solve a critical problem in modern applications: **handling continuous data streams safely**. Perfect for keeping "recent N items" automatically without memory bloat or performance degradation. @@ -16,35 +40,81 @@ Buffers solve a critical problem in modern applications: **handling continuous d - **O(1) Performance**: Lightning-fast operations regardless of data volume - **Zero Memory Leaks**: Automatic cleanup of old data - **Streaming Optimized**: Designed for continuous data flow +- **Flexible Ordering**: Choose FIFO or LIFO based on your needs + + +## FIFO Buffer Use Cases ### Chat Applications +This example demonstrates how to use a FIFO buffer to maintain a chat history, automatically removing the oldest messages when new ones are added. + ```smalltalk "Chat room - keep last 50 messages automatically" -chat := CTBuffer withCapacity: 50. +chat := CTFIFOBuffer withCapacity: 50. -chat put: 'User1: Hello everyone!'. -chat put: 'User2: How are you doing?'. +chat push: 'User1: Hello everyone!'. +chat push: 'User2: How are you doing?'. "... more messages flow in ..." -chat put: 'User3: Good morning!'. "Oldest message automatically removed" +chat push: 'User3: Good morning!'. "Oldest message automatically removed" "Always has recent 50 messages, zero manual cleanup" ``` ### File Processing in Chunks +This example shows how to process large files in manageable chunks without loading the entire file into memory, using a FIFO buffer to handle data efficiently. + ```smalltalk "Process massive files without loading everything into memory" -fileBuffer := CTBuffer withCapacity: 1024. "1KB processing chunks" +fileBuffer := CTFIFOBuffer withCapacity: 1024. "1KB processing chunks" stream := 'huge-dataset.csv' asFileReference readStream. [ stream atEnd ] whileFalse: [ chunk := stream next: 1024. - fileBuffer put: chunk. - self processDataChunk: fileBuffer get "Process and auto-remove" + fileBuffer push: chunk. + self processDataChunk: fileBuffer pop "Process and auto-remove" ]. "Memory usage stays constant - handles files of any size!" ``` +## LIFO Buffer Use Cases + +### Undo/Redo Functionality +This example shows how to implement undo/redo functionality in an editor using a LIFO buffer, allowing users to revert their last actions easily. + +```smalltalk +"Implementing undo/redo in an editor" +undoBuffer := CTLIFOBuffer withCapacity: 20. "Keep last 20 actions" +undoBuffer push: 'Hello World'. +undoBuffer push: 'Add bold formatting'. +undoBuffer push: 'Add italic formatting'. + +"User presses undo" +lastAction := undoBuffer pop. "Returns 'Add italic formatting' and removes it" + +"User can redo by pushing it back" +undoBuffer push: lastAction. "Puts 'Add italic formatting' back on top" +"Now 'Add italic formatting' is on top, ready to be undone next" +``` + +### Browser History +This example demonstrates how to implement a simple browser history using a LIFO buffer, allowing users to navigate back through their most recent pages. + +```smalltalk +"Browser back button - show most recent pages first" +browserHistory := CTLIFOBuffer withCapacity: 20. + +browserHistory push: 'https://pharo.org'. +browserHistory push: 'https://github.com/pharo-containers'. +browserHistory push: 'https://stackoverflow.com/questions/...'. + +"Back button gets most recent page" +previousPage := browserHistory pop. "Gets 'https://stackoverflow.com/questions/...' (most recent)" +previousPage := browserHistory pop. "Gets 'https://github.com/pharo-containers' (second most recent)" +previousPage := browserHistory pop. "Gets 'https://pharo.org' (third most recent)" +``` + + ### Performance Degradation ```smalltalk "OrderedCollection - Gets slower and slower" @@ -58,43 +128,20 @@ log := OrderedCollection new. "Performance degrades from 1ms to 100ms+ per operation" "Buffer - Lightning fast forever" -log := CTBuffer withCapacity: 1000. +log := CTFIFOBuffer withCapacity: 1000. 1 to: 100000 do: [ :i | - log put: 'entry ', i asString. "O(1) operation - always instant!" + log push: 'entry ', i asString. "O(1) operation - always instant!" ]. "Consistent 0.01ms performance whether it's operation #10 or #10,000,000" ``` - -### Performance Comparison - -| Operation | OrderedCollection | Array | CTBuffer | -|-----------|------------------|--------|----------| -| Add item | O(1) | O(n)* | O(1) | +### Comparison +| Operation | OrderedCollection | Array | CTFIFOBuffer | +|-----------|------------------|--------|-------------| +| Add item | O(1) | O(1) | O(1) | | Remove old | O(n) | O(n) | O(1) | | Memory usage | Unlimited growth | Fixed/reallocated | Fixed | -| Cache efficiency | Poor (fragmented) | Good | Excellent | - -## Loading -The following script installs Containers-Buffer in Pharo. - -```smalltalk -Metacello new - baseline: 'ContainersBuffer'; - repository: 'github://pharo-containers/Containers-Buffer/src'; - load. -``` - -## If you want to depend on it - -Add the following code to your Metacello baseline or configuration - -```smalltalk -spec - baseline: 'ContainersBuffer' - with: [ spec repository: 'github://pharo-containers/Containers-Buffer/src' ]. -``` ## Contributing