diff --git a/src/FileSystem-Core/FileReference.class.st b/src/FileSystem-Core/FileReference.class.st index 34cce466ad3..aeb64780ac3 100644 --- a/src/FileSystem-Core/FileReference.class.st +++ b/src/FileSystem-Core/FileReference.class.st @@ -465,6 +465,13 @@ FileReference >> size [ ^ filesystem sizeOf: path ] +{ #category : #streams } +FileReference >> unbufferedBinaryWriteStream [ + "Answer a binary read/write stream on the receiver" + + ^ filesystem binaryWriteStreamOn: self path +] + { #category : #streams } FileReference >> writeStream [ diff --git a/src/System-Sources/SourceFile.class.st b/src/System-Sources/SourceFile.class.st index 8150234c743..011ca231b60 100644 --- a/src/System-Sources/SourceFile.class.st +++ b/src/System-Sources/SourceFile.class.st @@ -241,14 +241,16 @@ SourceFile >> tryOpenReadOnly: readOnly [ readOnly ifFalse: [ potentialLocations do: [ :each | - [ stream := ZnCharacterReadWriteStream - on: (each asFileReference / basename) binaryReadWriteStream + [ stream := SourceFileCharacterReadWriteStream + on: (SourceFileBufferedReadWriteStream on:(each asFileReference / basename) unbufferedBinaryWriteStream) encoding: 'utf8'. ^ self ] on: Error do: [ ] ] ]. potentialLocations do: [ :each | - [ stream := (each asFileReference / basename) readStream. - ^ self ] on: Error do: [ ] ] + [ stream := ZnCharacterReadStream + on: (each asFileReference / basename) binaryReadStream + encoding: 'utf8'. + ^ self ] on: Error do: [ ] ] ] { #category : #accessing } diff --git a/src/System-Sources/SourceFileBufferedReadWriteStream.class.st b/src/System-Sources/SourceFileBufferedReadWriteStream.class.st new file mode 100644 index 00000000000..b0b8d9433c3 --- /dev/null +++ b/src/System-Sources/SourceFileBufferedReadWriteStream.class.st @@ -0,0 +1,343 @@ +" +I am Buffered Read Write Stream. +I should only be used in the SourceFile implementation +" +Class { + #name : #SourceFileBufferedReadWriteStream, + #superclass : #Object, + #instVars : [ + 'innerStream', + 'buffer', + 'bufferLength', + 'streamSize', + 'bufferOffset', + 'nextPosition', + 'isDirty' + ], + #category : #'System-Sources-Streams' +} + +{ #category : #'instance creation' } +SourceFileBufferedReadWriteStream class >> on: writeStream [ + ^ self basicNew + on: writeStream; + yourself +] + +{ #category : #convenience } +SourceFileBufferedReadWriteStream class >> on: readStream do: block [ + "Execute block with as argument a ZnBufferedReadStream on readStream. + Return the value of block." + + | stream | + + stream := self on: readStream. + + ^ [block value: stream ] ensure: [ stream flush ] +] + +{ #category : #testing } +SourceFileBufferedReadWriteStream >> atEnd [ + + ^ self atEnd: nextPosition +] + +{ #category : #private } +SourceFileBufferedReadWriteStream >> atEnd: anInteger [ + + anInteger < streamSize ifTrue: [ ^ false ]. + anInteger <= (bufferOffset + bufferLength) ifTrue: [ ^ false ]. + + ^ true +] + +{ #category : #private } +SourceFileBufferedReadWriteStream >> bufferAt: aPosition [ + + ^ buffer at: (aPosition - bufferOffset) + + +] + +{ #category : #private } +SourceFileBufferedReadWriteStream >> bufferAt: aPosition put: anElement [ + + self checkBufferFor: nextPosition. + + bufferLength := (aPosition - bufferOffset) max: bufferLength. + buffer at: (aPosition - bufferOffset) put: anElement + +] + +{ #category : #private } +SourceFileBufferedReadWriteStream >> checkBufferFor: aPosition [ + + (self isPositionInBuffer: aPosition) + ifFalse: [ self refreshBufferFrom: aPosition ] +] + +{ #category : #closing } +SourceFileBufferedReadWriteStream >> close [ + + self flush. + innerStream close +] + +{ #category : #testing } +SourceFileBufferedReadWriteStream >> closed [ + + ^ innerStream closed +] + +{ #category : #'initialize-release' } +SourceFileBufferedReadWriteStream >> collectionSpecies [ + ^ innerStream isBinary + ifTrue: [ ByteArray ] + ifFalse: [ String ] +] + +{ #category : #initialization } +SourceFileBufferedReadWriteStream >> defaultBufferSize [ + + ^ 2 raisedToInteger: 16 +] + +{ #category : #writing } +SourceFileBufferedReadWriteStream >> flush [ + + isDirty ifFalse: [ ^ self ]. + + innerStream position: bufferOffset. + innerStream next: bufferLength putAll: buffer startingAt: 1. + + innerStream flush. + + streamSize := innerStream size. + + isDirty := false +] + +{ #category : #testing } +SourceFileBufferedReadWriteStream >> isBinary [ + + ^ innerStream isBinary +] + +{ #category : #private } +SourceFileBufferedReadWriteStream >> isPositionInBuffer: aPosition [ + + ^ aPosition between: bufferOffset and: bufferOffset + bufferLength +] + +{ #category : #testing } +SourceFileBufferedReadWriteStream >> isReadOnly [ + + ^ false +] + +{ #category : #testing } +SourceFileBufferedReadWriteStream >> isStream [ + + ^ true +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> next [ + | value | + + self atEnd + ifTrue: [^ nil]. + + self checkBufferFor:nextPosition. + + value := self bufferAt: nextPosition. + + nextPosition := nextPosition + 1. + + ^ value +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> next: aQuantity [ + + | read collection | + + collection := self collectionSpecies new: aQuantity. + + read := self + readInto: collection + startingAt: 1 + count: aQuantity. + + ^ read = aQuantity + ifTrue: [ collection ] + ifFalse: [ collection copyFrom: 1 to: read - 1 ] +] + +{ #category : #writing } +SourceFileBufferedReadWriteStream >> next: aQuantity putAll: aCollection startingAt: startingAt [ + + aCollection readStreamDo: [ :s | + s skip: startingAt - 1. + self nextPutAll: (s next: aQuantity)]. +] + +{ #category : #writing } +SourceFileBufferedReadWriteStream >> nextPut: anElement [ + + self checkBufferFor: nextPosition. + + self bufferAt: nextPosition put: anElement. + + isDirty := true. + nextPosition := nextPosition + 1 +] + +{ #category : #writing } +SourceFileBufferedReadWriteStream >> nextPutAll: aCollection [ + + aCollection do: [ :each | self nextPut: each ] +] + +{ #category : #'instance creation' } +SourceFileBufferedReadWriteStream >> on: aStream [ + + innerStream := aStream. + nextPosition := aStream position + 1. + streamSize := aStream size. + + bufferOffset := -1. + bufferLength := 0. + isDirty := false. + + self sizeBuffer: self defaultBufferSize. + + +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> peek [ + + | value | + value := self next. + "If I have read correctly I reset the position" + value ifNotNil: [ nextPosition := nextPosition - 1 ]. + + ^ value + +] + +{ #category : #querying } +SourceFileBufferedReadWriteStream >> position [ + + ^ nextPosition - 1 + +] + +{ #category : #querying } +SourceFileBufferedReadWriteStream >> position: aNewPosition [ + + ^ nextPosition := aNewPosition + 1 + +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> readInto: aBuffer startingAt: startingAt count: count [ + + | remainingCount maxPositionInBuffer read countToRead | + + remainingCount := count. + read := 0. + + [ remainingCount > 0 and: [ self atEnd not ]] + whileTrue: [ + self checkBufferFor: nextPosition. + + maxPositionInBuffer := bufferOffset + bufferLength. + countToRead := ( maxPositionInBuffer - (nextPosition - 1) ) min: remainingCount. + aBuffer + replaceFrom: startingAt + read + to: startingAt + read + countToRead - 1 + with: buffer + startingAt: (nextPosition - bufferOffset). + + nextPosition := nextPosition + countToRead. + remainingCount := remainingCount - countToRead. + read := read + countToRead + ]. + + ^ read. +] + +{ #category : #private } +SourceFileBufferedReadWriteStream >> refreshBufferFrom: aPosition [ + + | nextBufferPosition | + + nextBufferPosition := (((aPosition - 1) max:0) // buffer size) * buffer size. + bufferOffset = nextBufferPosition ifTrue: [ ^ self ]. + + self flush. + + "If the position is outside the real stream I will only flush the buffer if I have to empty it." + (nextBufferPosition >= streamSize) + ifTrue: [ + bufferOffset := nextBufferPosition. + bufferLength := 0. + ^ self ]. + + (nextBufferPosition = (bufferOffset + bufferLength)) + ifFalse: [ innerStream position: nextBufferPosition ]. + + bufferLength := innerStream readInto: buffer startingAt: 1 count: buffer size. + bufferOffset := nextBufferPosition. +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> setToEnd [ + + nextPosition := (streamSize max: (bufferOffset + bufferLength)) + 1 +] + +{ #category : #querying } +SourceFileBufferedReadWriteStream >> size [ + + ^ streamSize max: (bufferOffset + bufferLength) +] + +{ #category : #'initialize-release' } +SourceFileBufferedReadWriteStream >> sizeBuffer: size [ + + bufferLength > 0 ifTrue: [ self flush ]. + bufferLength := 0. + + buffer := self collectionSpecies new: size +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> skip: aQuantity [ + + nextPosition := nextPosition + aQuantity +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> upTo: value [ + "Read upto but not including value and return them as a collection. + If value is not found, return the entire contents of the stream. + This could be further optimzed." + + ^ self collectionSpecies + streamContents: [ :writeStream | | element | + [ self atEnd or: [ (element := self next) = value ] ] whileFalse: [ + writeStream nextPut: element ] ] +] + +{ #category : #reading } +SourceFileBufferedReadWriteStream >> upToEnd [ + "Read elements until the stream is atEnd and return them as a collection." + + | toRead | + + toRead := (streamSize - (nextPosition - 1)) max: 0. + ^ self next: toRead. +] diff --git a/src/System-Sources/SourceFileCharacterReadWriteStream.class.st b/src/System-Sources/SourceFileCharacterReadWriteStream.class.st new file mode 100644 index 00000000000..db3449bb9f2 --- /dev/null +++ b/src/System-Sources/SourceFileCharacterReadWriteStream.class.st @@ -0,0 +1,181 @@ +" +I am a read-write character stream. I am mainly used to open the Pharo source and changes files. +" +Class { + #name : #SourceFileCharacterReadWriteStream, + #superclass : #Object, + #instVars : [ + 'readStream', + 'writeStream' + ], + #category : #'System-Sources-Streams' +} + +{ #category : #'instance creation' } +SourceFileCharacterReadWriteStream class >> on: wrappedStream encoding: encoding [ + + ^ self new + on: wrappedStream encoding: encoding; + yourself +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> atEnd [ + + ^ readStream atEnd +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> close [ + + writeStream close +] + +{ #category : #testing } +SourceFileCharacterReadWriteStream >> closed [ + ^ writeStream closed +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> collectionSpecies [ + ^ String +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> cr [ + + writeStream cr +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> flush [ + + writeStream flush +] + +{ #category : #testing } +SourceFileCharacterReadWriteStream >> isReadOnly [ + + ^ false +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> next [ + + ^ readStream next +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> next: anInteger [ + + ^ readStream next: anInteger +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> nextPut: aCharacter [ + + ^ writeStream nextPut: aCharacter +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> nextPutAll: aString [ + + ^ writeStream nextPutAll: aString +] + +{ #category : #'instance creation' } +SourceFileCharacterReadWriteStream >> on: aStream encoding: encoding [ + | encoder | + encoder := encoding asZnCharacterEncoder. + readStream := ZnCharacterReadStream on: aStream encoding: encoder. + writeStream := ZnCharacterWriteStream on: aStream encoding: encoder +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> peek [ + + ^ readStream peek +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> position [ + + ^ readStream position +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> position: anInteger [ + + readStream position: anInteger +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> print: object [ + writeStream print: object +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> readOnlyCopy [ + + ^ readStream +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> setToEnd [ + + writeStream setToEnd +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> size [ + + ^ readStream size +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> skip: anInteger [ + + readStream skip: anInteger +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> space [ + + writeStream space +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> tab [ + + writeStream tab +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> upToAll: aCollection [ + "Answer a subcollection from the current access position to the occurrence (if any, but not inclusive) of aCollection. If aCollection is not in the stream, answer the entire rest of the stream." + + ^ self collectionSpecies streamContents: [ :out | + | partialMatch pattern matched | + partialMatch := (self collectionSpecies new: aCollection size) writeStream. + pattern := aCollection readStream. + matched := false. + ([ matched or: [ self atEnd or: [ pattern atEnd ] ] ]) whileFalse: [ + | ch | + (ch := self next) = pattern next + ifTrue: [ + pattern atEnd + ifTrue: [ matched := true ] + ifFalse: [ partialMatch nextPut: ch ] ] + ifFalse: [ + pattern reset. + out nextPutAll: partialMatch contents. + partialMatch reset. + out nextPut: ch ] ]. + matched ifFalse: [ out nextPutAll: partialMatch contents ] ] +] + +{ #category : #accessing } +SourceFileCharacterReadWriteStream >> upToEnd [ + + ^ readStream upToEnd +] diff --git a/src/Tests/SourceFileArrayTest.class.st b/src/Tests/SourceFileArrayTest.class.st index b5a8c75ddcd..85d38a1acf3 100644 --- a/src/Tests/SourceFileArrayTest.class.st +++ b/src/Tests/SourceFileArrayTest.class.st @@ -377,10 +377,10 @@ SourceFileArrayTest >> testWriteToBufferedStream [ array := SourceFileArray new. array changesFileStream: - (ZnCharacterReadWriteStream on: (ZnBufferedReadWriteStream on: (fs binaryWriteStreamOn: (fs / 'changes.chunk') path)) encoding: #utf8). + (SourceFileCharacterReadWriteStream on: (SourceFileBufferedReadWriteStream on: (fs binaryWriteStreamOn: (fs / 'changes.chunk') path)) encoding: #utf8). array sourcesFileStream: - (ZnCharacterReadWriteStream on: (ZnBufferedReadWriteStream on: (fs binaryWriteStreamOn: (fs / 'sources.chunk') path)) encoding: #utf8). + (SourceFileCharacterReadWriteStream on: (SourceFileBufferedReadWriteStream on: (fs binaryWriteStreamOn: (fs / 'sources.chunk') path)) encoding: #utf8). array writeSource: 'some source' diff --git a/src/Tests/SourceFileBufferedReadWriteStreamTest.class.st b/src/Tests/SourceFileBufferedReadWriteStreamTest.class.st new file mode 100644 index 00000000000..3e6855cf164 --- /dev/null +++ b/src/Tests/SourceFileBufferedReadWriteStreamTest.class.st @@ -0,0 +1,180 @@ +Class { + #name : #SourceFileBufferedReadWriteStreamTest, + #superclass : #TestCase, + #category : #Tests +} + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testBuffering [ + | stream | + stream := SourceFileBufferedReadWriteStream on: '01234567890123456789' readStream. + stream sizeBuffer: 8. + self deny: stream atEnd. + self assert: (stream next: 10) equals: '0123456789'. + self deny: stream atEnd. + self assert: (stream next: 10) equals: '0123456789'. + self assert: stream atEnd +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testNextPutAllStartingAt [ + | stringStream | + + stringStream := ReadWriteStream with: ''. + + SourceFileBufferedReadWriteStream on: stringStream do: [ : bufferedStream | + bufferedStream sizeBuffer: 8. + bufferedStream next: 5 putAll: '--012345--' startingAt: 3. + bufferedStream next: 5 putAll: '0123456789XX' startingAt: 6. + bufferedStream next: 5 putAll: '--012345--' startingAt: 3. + bufferedStream next: 5 putAll: '0123456789XX' startingAt: 6.] . + + self assert: stringStream contents equals: '01234567890123456789' +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testPeek [ + | stream | + stream := SourceFileBufferedReadWriteStream on: '0123456789' readStream. + stream sizeBuffer: 8. + '0123456789' do: [ :each | + self deny: stream atEnd. + self assert: stream peek equals: each. + self assert: stream next equals: each ]. + self assert: stream atEnd. + self assert: stream peek isNil. + self assert: stream next isNil +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testReadInto [ + | stream buffer count | + stream := SourceFileBufferedReadWriteStream on: '0123456789' readStream. + stream sizeBuffer: 8. + buffer := String new: 6 withAll: Character space. + stream skip: 1. + stream readInto: buffer startingAt: 4 count: 3. + self assert: buffer equals: ' 123'. + stream readInto: buffer startingAt: 1 count: 3. + self assert: buffer equals: '456123'. + count := stream readInto: buffer startingAt: 1 count: 100. + self assert: count equals: 3. + self assert: buffer equals: '789123' +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testReadIntoLarger [ + | stream buffer count | + stream := SourceFileBufferedReadWriteStream on: '0123456789' readStream. + stream sizeBuffer: 4. + buffer := String new: 10. + count := stream readInto: buffer startingAt: 1 count: 10. + self assert: count equals: 10. + self assert: buffer equals: '0123456789' +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testReadThenWrite [ + | stream stringStream | + + stringStream := ReadWriteStream with: '0123456789'. + stringStream reset. + stream := SourceFileBufferedReadWriteStream on: stringStream. + stream sizeBuffer: 8. + + stream next: 4. + self assert: stream position equals: 4. + + stream nextPutAll: 'ABCD'. + self assert: stream position equals: 8. + + self assert: stream peek equals: $8. + self assert: stream upToEnd equals: '89'. + self assert: stream atEnd. + + self assert: stringStream contents equals: '0123ABCD89' +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testReadUpTo [ + | stream | + stream := SourceFileBufferedReadWriteStream on: '0123456789' readStream. + stream sizeBuffer: 8. + self assert: (stream upTo: $5) equals: '01234'. + self assert: stream upToEnd equals: '6789'. + self assert: stream atEnd +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testReadUpToEnd [ + | stream | + stream := SourceFileBufferedReadWriteStream on: '0123456789' readStream. + stream sizeBuffer: 4. + stream next: 2. + self assert: stream upToEnd equals: '23456789'. + self assert: stream atEnd +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testSetAtEnd [ + + | stream originalStream| + + stream := SourceFileBufferedReadWriteStream on: (originalStream := ReadWriteStream with: '0123456789'). + originalStream reset. + + stream setToEnd. + + stream nextPut: $A. + stream flush. + + self assert: originalStream contents equals: '0123456789A'. + +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testWriteThenRead [ + | stream stringStream | + + stringStream := ReadWriteStream with: '0123456789'. + stringStream reset. + stream := SourceFileBufferedReadWriteStream on: stringStream. + stream sizeBuffer: 8. + + stream nextPutAll: 'ABCD'. + + self assert: stream peek equals: $4. + self assert: stream position equals: 4. + self assert: stream upToEnd equals: '456789'. + self assert: stream position equals: 10. + self assert: stream atEnd. + + self assert: stringStream contents equals: 'ABCD456789' +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testWriting [ + | stringStream bufferedStream | + stringStream := ReadWriteStream with: ''. + + bufferedStream := SourceFileBufferedReadWriteStream on: stringStream. + 0 to: 9 do: [ :each | bufferedStream nextPut: (Character digitValue: each) ]. + bufferedStream flush. + + self assert: stringStream contents = '0123456789' +] + +{ #category : #tests } +SourceFileBufferedReadWriteStreamTest >> testWritingOverflow [ + | stringStream bufferedStream | + + stringStream := ReadWriteStream with: ''. + bufferedStream := SourceFileBufferedReadWriteStream on: stringStream. + bufferedStream sizeBuffer: 8. + 0 to: 9 do: [ :each | bufferedStream nextPut: (Character digitValue: each) ]. + bufferedStream nextPutAll: '0123'; nextPutAll: '4567'; nextPutAll: '89'. + bufferedStream nextPutAll: '0123456789'; nextPutAll: '0123456789'. + bufferedStream flush. + + self assert: stringStream contents equals: '0123456789012345678901234567890123456789' +] diff --git a/src/Zinc-Character-Encoding-Core/ZnBufferedReadWriteStream.class.st b/src/Zinc-Character-Encoding-Core/ZnBufferedReadWriteStream.class.st index 803c48da2ba..4e5fb3fe3b6 100644 --- a/src/Zinc-Character-Encoding-Core/ZnBufferedReadWriteStream.class.st +++ b/src/Zinc-Character-Encoding-Core/ZnBufferedReadWriteStream.class.st @@ -15,13 +15,9 @@ Class { #name : #ZnBufferedReadWriteStream, #superclass : #Object, #instVars : [ - 'innerStream', - 'buffer', - 'bufferLength', - 'streamSize', - 'bufferOffset', - 'nextPosition', - 'isDirty' + 'readStream', + 'writeStream', + 'lastRead' ], #category : #'Zinc-Character-Encoding-Core' } @@ -38,44 +34,13 @@ ZnBufferedReadWriteStream class >> on: readStream do: block [ "Execute block with as argument a ZnBufferedReadStream on readStream. Return the value of block." - | stream | - - stream := self on: readStream. - - ^ [block value: stream ] ensure: [ stream flush ] + ^ block value: (self on: readStream) ] { #category : #testing } ZnBufferedReadWriteStream >> atEnd [ - ^ self atEnd: nextPosition -] - -{ #category : #private } -ZnBufferedReadWriteStream >> atEnd: anInteger [ - - anInteger < streamSize ifTrue: [ ^ false ]. - anInteger <= (bufferOffset + bufferLength) ifTrue: [ ^ false ]. - - ^ true -] - -{ #category : #private } -ZnBufferedReadWriteStream >> bufferAt: aPosition [ - - ^ buffer at: (aPosition - bufferOffset) - - -] - -{ #category : #private } -ZnBufferedReadWriteStream >> bufferAt: aPosition put: anElement [ - - self checkBufferFor: nextPosition. - - bufferLength := (aPosition - bufferOffset) max: bufferLength. - buffer at: (aPosition - bufferOffset) put: anElement - + ^ self readingActionDo: [ readStream atEnd ] ] { #category : #private } @@ -85,24 +50,16 @@ ZnBufferedReadWriteStream >> checkBufferFor: aPosition [ ifFalse: [ self refreshBufferFrom: aPosition ] ] -{ #category : #closing } +{ #category : #accessing } ZnBufferedReadWriteStream >> close [ - self flush. - innerStream close + writeStream flush. + writeStream close. ] { #category : #testing } ZnBufferedReadWriteStream >> closed [ - - ^ innerStream closed -] - -{ #category : #'initialize-release' } -ZnBufferedReadWriteStream >> collectionSpecies [ - ^ innerStream isBinary - ifTrue: [ ByteArray ] - ifFalse: [ String ] + ^ readStream closed ] { #category : #initialization } @@ -111,31 +68,16 @@ ZnBufferedReadWriteStream >> defaultBufferSize [ ^ 2 raisedToInteger: 16 ] -{ #category : #writing } +{ #category : #accessing } ZnBufferedReadWriteStream >> flush [ - isDirty ifFalse: [ ^ self ]. - - innerStream position: bufferOffset. - innerStream next: bufferLength putAll: buffer startingAt: 1. - - innerStream flush. - - streamSize := innerStream size. - - isDirty := false + self writingActionDo: [ writeStream flush ] ] { #category : #testing } ZnBufferedReadWriteStream >> isBinary [ - ^ innerStream isBinary -] - -{ #category : #private } -ZnBufferedReadWriteStream >> isPositionInBuffer: aPosition [ - - ^ aPosition between: bufferOffset and: bufferOffset + bufferLength + ^ readStream isBinary ] { #category : #testing } @@ -150,203 +92,164 @@ ZnBufferedReadWriteStream >> isStream [ ^ true ] -{ #category : #reading } -ZnBufferedReadWriteStream >> next [ - | value | - - self atEnd - ifTrue: [^ nil]. - - self checkBufferFor:nextPosition. - - value := self bufferAt: nextPosition. +{ #category : #accessing } +ZnBufferedReadWriteStream >> next [ - nextPosition := nextPosition + 1. - - ^ value + ^ self readingActionDo: [ + readStream next ] ] -{ #category : #reading } -ZnBufferedReadWriteStream >> next: aQuantity [ - - | read collection | - - collection := self collectionSpecies new: aQuantity. - - read := self - readInto: collection - startingAt: 1 - count: aQuantity. - - ^ read = aQuantity - ifTrue: [ collection ] - ifFalse: [ collection copyFrom: 1 to: read - 1 ] +{ #category : #accessing } +ZnBufferedReadWriteStream >> next: anInteger [ + + ^ self readingActionDo: [ + readStream next: anInteger ] ] -{ #category : #writing } -ZnBufferedReadWriteStream >> next: aQuantity putAll: aCollection startingAt: startingAt [ +{ #category : #accessing } +ZnBufferedReadWriteStream >> next: count putAll: collection [ - aCollection readStreamDo: [ :s | - s skip: startingAt - 1. - self nextPutAll: (s next: aQuantity)]. + self writingActionDo: [ + writeStream next: count putAll: collection ] ] -{ #category : #writing } -ZnBufferedReadWriteStream >> nextPut: anElement [ +{ #category : #accessing } +ZnBufferedReadWriteStream >> next: count putAll: collection startingAt: offset [ - self checkBufferFor: nextPosition. - - self bufferAt: nextPosition put: anElement. + self writingActionDo: [ + writeStream next: count putAll: collection startingAt: offset ] +] + +{ #category : #accessing } +ZnBufferedReadWriteStream >> nextPut: aCharacter [ - isDirty := true. - nextPosition := nextPosition + 1 + self writingActionDo: [ writeStream nextPut: aCharacter ] ] -{ #category : #writing } -ZnBufferedReadWriteStream >> nextPutAll: aCollection [ +{ #category : #accessing } +ZnBufferedReadWriteStream >> nextPutAll: aString [ - aCollection do: [ :each | self nextPut: each ] + ^ self writingActionDo: [ writeStream nextPutAll: aString ] ] { #category : #'instance creation' } ZnBufferedReadWriteStream >> on: aStream [ - innerStream := aStream. - nextPosition := aStream position + 1. - streamSize := aStream size. - - bufferOffset := -1. - bufferLength := 0. - isDirty := false. - - self sizeBuffer: self defaultBufferSize. - - + lastRead := true. + readStream := ZnBufferedReadStream on: aStream. + writeStream := ZnBufferedWriteStream on: aStream. ] -{ #category : #reading } +{ #category : #accessing } ZnBufferedReadWriteStream >> peek [ - | value | - value := self next. - "If I have read correctly I reset the position" - value ifNotNil: [ nextPosition := nextPosition - 1 ]. - - ^ value - + ^ self readingActionDo: [ + readStream peek ] ] -{ #category : #querying } +{ #category : #accessing } ZnBufferedReadWriteStream >> position [ - - ^ nextPosition - 1 - + + ^ lastRead + ifTrue: [ readStream position ] + ifFalse: [ writeStream position ] ] -{ #category : #querying } -ZnBufferedReadWriteStream >> position: aNewPosition [ - - ^ nextPosition := aNewPosition + 1 - +{ #category : #accessing } +ZnBufferedReadWriteStream >> position: anInteger [ + + self writingActionDo: [ + writeStream position: anInteger ] + ] -{ #category : #reading } -ZnBufferedReadWriteStream >> readInto: aBuffer startingAt: startingAt count: count [ - - | remainingCount maxPositionInBuffer read countToRead | - - remainingCount := count. - read := 0. +{ #category : #accessing } +ZnBufferedReadWriteStream >> readInto: collection startingAt: offset count: requestedCount [ - [ remainingCount > 0 and: [ self atEnd not ]] - whileTrue: [ - self checkBufferFor: nextPosition. - - maxPositionInBuffer := bufferOffset + bufferLength. - countToRead := ( maxPositionInBuffer - (nextPosition - 1) ) min: remainingCount. - aBuffer - replaceFrom: startingAt + read - to: startingAt + read + countToRead - 1 - with: buffer - startingAt: (nextPosition - bufferOffset). - - nextPosition := nextPosition + countToRead. - remainingCount := remainingCount - countToRead. - read := read + countToRead - ]. - - ^ read. + ^ self readingActionDo: [ + readStream readInto: collection startingAt: offset count: requestedCount ] ] { #category : #private } -ZnBufferedReadWriteStream >> refreshBufferFrom: aPosition [ - - | nextBufferPosition | +ZnBufferedReadWriteStream >> readingActionDo: aBlock [ - nextBufferPosition := (((aPosition - 1) max:0) // buffer size) * buffer size. - bufferOffset = nextBufferPosition ifTrue: [ ^ self ]. - - self flush. - - "If the position is outside the real stream I will only flush the buffer if I have to empty it." - (nextBufferPosition >= streamSize) - ifTrue: [ - bufferOffset := nextBufferPosition. - bufferLength := 0. - ^ self ]. - - (nextBufferPosition = (bufferOffset + bufferLength)) - ifFalse: [ innerStream position: nextBufferPosition ]. - - bufferLength := innerStream readInto: buffer startingAt: 1 count: buffer size. - bufferOffset := nextBufferPosition. + "Reading from the read stream. + We should + - flush the write stream + - discard the read buffer (which may contain incorrect data). + - and then perform the read." + + lastRead ifFalse: [ + writeStream flush. + readStream discardBuffer ]. + ^ aBlock ensure: [ lastRead := true ] ] -{ #category : #reading } +{ #category : #accessing } ZnBufferedReadWriteStream >> setToEnd [ - - nextPosition := (streamSize max: (bufferOffset + bufferLength)) + 1 + + ^ self writingActionDo: [ + writeStream setToEnd ] ] -{ #category : #querying } -ZnBufferedReadWriteStream >> size [ - - ^ streamSize max: (bufferOffset + bufferLength) +{ #category : #accessing } +ZnBufferedReadWriteStream >> size [ + ^ readStream size ] { #category : #'initialize-release' } -ZnBufferedReadWriteStream >> sizeBuffer: size [ - - bufferLength > 0 ifTrue: [ self flush ]. - bufferLength := 0. +ZnBufferedReadWriteStream >> sizeBuffer: anInteger [ + + readStream sizeBuffer: anInteger. + writeStream sizeBuffer: anInteger. +] - buffer := self collectionSpecies new: size +{ #category : #accessing } +ZnBufferedReadWriteStream >> skip: anInteger [ + + self readingActionDo: [ + readStream skip: anInteger ] ] -{ #category : #reading } -ZnBufferedReadWriteStream >> skip: aQuantity [ +{ #category : #accessing } +ZnBufferedReadWriteStream >> truncate [ + + self writingActionDo: [ writeStream truncate ] +] - nextPosition := nextPosition + aQuantity +{ #category : #accessing } +ZnBufferedReadWriteStream >> truncate: anInteger [ + + self writingActionDo: [ writeStream truncate: anInteger ] ] -{ #category : #reading } -ZnBufferedReadWriteStream >> upTo: value [ - "Read upto but not including value and return them as a collection. - If value is not found, return the entire contents of the stream. - This could be further optimzed." +{ #category : #accessing } +ZnBufferedReadWriteStream >> upTo: aCharacter [ - ^ self collectionSpecies - streamContents: [ :writeStream | | element | - [ self atEnd or: [ (element := self next) = value ] ] whileFalse: [ - writeStream nextPut: element ] ] + ^ self readingActionDo: [ readStream upTo: aCharacter ] ] -{ #category : #reading } +{ #category : #accessing } ZnBufferedReadWriteStream >> upToEnd [ - "Read elements until the stream is atEnd and return them as a collection." + + ^ self readingActionDo: [ readStream upToEnd ] +] + +{ #category : #accessing } +ZnBufferedReadWriteStream >> wrappedStream [ - | toRead | + ^ readStream wrappedStream +] - toRead := (streamSize - (nextPosition - 1)) max: 0. - ^ self next: toRead. +{ #category : #accessing } +ZnBufferedReadWriteStream >> writingActionDo: aBlock [ + + "Writing to the write stream. + We should + - write the write stream + - discard the read buffer (which may contain incorrect data)" + lastRead ifTrue: [ + writeStream discardBuffer ]. + readStream discardBuffer. + ^ aBlock ensure: [ lastRead := false ] ]