--- a/PositionableStream.st Tue Feb 04 21:09:59 2014 +0100
+++ b/PositionableStream.st Wed Apr 01 10:20:10 2015 +0100
@@ -1,3 +1,5 @@
+"{ Encoding: utf8 }"
+
"
COPYRIGHT (c) 1989 by Claus Gittinger
All Rights Reserved
@@ -11,6 +13,8 @@
"
"{ Package: 'stx:libbasic' }"
+"{ NameSpace: Smalltalk }"
+
PeekableStream subclass:#PositionableStream
instanceVariableNames:'collection position readLimit writeLimit'
classVariableNames:'ZeroPosition InvalidPositionErrorSignal'
@@ -46,9 +50,14 @@
In previous versions of ST/X, streams started with a 1-position (i.e. as in collections),
while ST-80 has always been using 0-based postions for streams and 1-based positions for collections.
- THIS CERTAINLY IS BAD.
+ THIS CERTAINLY IS BAD.
+
+ Although this is confusing ST/X has been changed to now also uses 0-based stream positioning.
- Although this is confusing ST/X has been changed to now also use 0-based stream positioning.
+ [caveat:]
+ Basing capabilities like readability/writability/positionability/peekability on inheritance makes
+ the class hierarchy ugly and leads to strange and hard to teach redefinitions (aka. NonPositionableStream
+ below PositionableStream or ExternalReadStream under WriteStream)
[author:]
Claus Gittinger
@@ -116,6 +125,7 @@
"Modified: / 13-07-2006 / 20:36:54 / cg"
! !
+
!PositionableStream methodsFor:'Compatibility-Dolphin'!
endChunk
@@ -141,6 +151,29 @@
"
! !
+!PositionableStream methodsFor:'Compatibility-Squeak'!
+
+back
+ "Go back one element and return it."
+
+ self position = 0 ifTrue: [self positionError].
+ self skip: -1.
+ ^ self peek
+
+ "Created: / 03-10-2014 / 03:06:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+peekBack
+ "Return the element at the previous position, without changing position. Use indirect messages in case self is a StandardFileStream."
+
+ | element |
+ element := self back.
+ self skip: 1.
+ ^ element
+
+ "Created: / 03-10-2014 / 03:06:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
!PositionableStream methodsFor:'accessing'!
collection
@@ -220,6 +253,35 @@
"Modified: / 04-06-2007 / 17:21:55 / cg"
! !
+!PositionableStream methodsFor:'misc functions'!
+
+copyToEndInto:aWriteStream bufferSize:bufferSize
+ "read from the receiver, and write all data up to the end to another stream.
+ Return the number of bytes which have been transferred.
+ Redefined here to avoid intermediate buffers/garbage.
+ bufferSize does not matter here."
+
+ |cnt|
+
+ collection notNil ifTrue:[
+ aWriteStream nextPutAll:collection startingAt:position+1 to:readLimit.
+ cnt := readLimit - position.
+ position := readLimit.
+ ^ cnt.
+ ].
+ ^ super copyToEndInto:aWriteStream bufferSize:bufferSize.
+
+ "
+ |rs ws cnt|
+
+ ws := #() writeStream.
+ rs := #( 1 2 3 4 a nil true) readWriteStream.
+ rs next.
+ cnt := rs copyToEndInto:ws bufferSize:0.
+ Transcript show:cnt; show:' '; showCR:ws contents.
+ "
+! !
+
!PositionableStream methodsFor:'positioning'!
backStep
@@ -228,6 +290,34 @@
self position:(self position - 1)
!
+match: subCollection
+ "Set the access position of the receiver to be past the next occurrence of the subCollection.
+ Answer whether subCollection is found.
+ No wildcards, and case does matter."
+
+ | pattern startMatch |
+
+ pattern := ReadStream on: subCollection.
+ startMatch := nil.
+ [pattern atEnd] whileFalse: [
+ self atEnd ifTrue: [^ false].
+ (self next) = (pattern next)
+ ifTrue: [pattern position = 1 ifTrue: [startMatch := self position]]
+ ifFalse: [
+ pattern position: 0.
+ startMatch ifNotNil: [
+ self position: startMatch.
+ startMatch := nil
+ ]
+ ]
+ ].
+ ^ true
+
+ "
+ 'abc def ghi' readStream match:'def'; upToEnd
+ "
+!
+
position
"return the read position (0-based)"
@@ -353,18 +443,23 @@
|buffer len first idx|
+ self isPositionable ifFalse:[
+ "/ for non-positionable subclasses
+ ^ super skipThroughAll:aCollection
+ ].
+
len := aCollection size.
first := aCollection at:1.
[self atEnd] whileFalse:[
buffer := self nextAvailable:len.
- buffer = aCollection ifTrue:[
- ^ self
- ].
buffer size == len ifTrue:[
+ buffer = aCollection ifTrue:[
+ ^ self
+ ].
"expect more input"
idx := buffer indexOf:first startingAt:2.
- idx == 0 ifFalse:[
- self position:(self position - len + idx - 1)
+ idx ~~ 0 ifTrue:[
+ self skip:(idx - len - 1)
].
].
].
@@ -524,19 +619,53 @@
"
! !
+!PositionableStream methodsFor:'queries'!
+
+endsBeforePositionWith:aSequenceableCollection
+ "answer true, if the elements in aSequenceableCollection
+ are at the current end of the stream up to position."
+
+ |sz pos|
+
+ sz := aSequenceableCollection size.
+ pos := self position.
+ pos < sz ifTrue:[
+ ^ false.
+ ].
+ self contentsSpecies == collection class ifTrue:[
+ ^ collection sameContentsFrom:pos+1-sz to:pos as:aSequenceableCollection startingAt:1.
+ ].
+ self position:pos-sz.
+ ^ (self next:sz) = aSequenceableCollection.
+
+ "
+ ('' writeStream nextPutAll:'Hello World') endsBeforePositionWith:'World'
+ ('' writeStream nextPutAll:'Hello World') endsBeforePositionWith:'Hello World'
+ ('' writeStream nextPutAll:'Hello World') endsBeforePositionWith:'Hello Worldx'
+ ('' writeStream nextPutAll:'Hello World') endsBeforePositionWith:'Bla'
+ ('' writeStream) endsBeforePositionWith:'Bla'
+ ('' writeStream) endsBeforePositionWith:''
+ '' endsWith:''
+ "
+! !
+
!PositionableStream methodsFor:'reading'!
nextAvailable:count
- |end result|
+ collection notNil ifTrue:[
+ |end result|
- end := position + count.
- end >= readLimit ifTrue:[
- end := readLimit.
+ end := position + count.
+ end >= readLimit ifTrue:[
+ end := readLimit.
+ ].
+
+ result := collection copyFrom:position+1 to:end.
+ position := end.
+ ^ result.
].
- result := collection copyFrom:position+1 to:end.
- position := end.
- ^ result.
+ ^ super nextAvailable:count.
"
'abc' readStream nextAvailable:1.
@@ -549,9 +678,81 @@
"
!
+nextAvailable:count into:aCollection startingAt:initialIndex
+ "return the next count objects from the stream. If the end is
+ reached before, only that many objects are copyied into the
+ collection.
+ Returns the number of objects that have been actually read."
+
+ | max |
+
+ collection notNil ifTrue:[
+ max := (readLimit - position) min: count.
+ aCollection
+ replaceFrom: initialIndex
+ to: initialIndex+max-1
+ with: collection
+ startingAt: position+1.
+ position := position + max.
+ ^ max.
+ ].
+
+ ^ super nextAvailable:count into:aCollection startingAt:initialIndex.
+
+ "
+ |s n buffer|
+
+ buffer := ByteArray new:10.
+
+ s := ReadStream on:#[1 2 3 4 5 6 7 8 9].
+ s next:3.
+ n := s nextBytes:9 into:buffer startingAt:1.
+ Transcript showCR:('n = %1; buffer = <%2>' bindWith:n with:buffer)
+ "
+!
+
+nextBytes:numBytes into:aCollection startingAt:initialIndex
+ "return the next numBytes from the stream. If the end is
+ reached before, only that many bytes are copyied into the
+ collection.
+ Returns the number of bytes that have been actually read.
+ The receiver must support reading of binary bytes.
+
+ Notice: this method is provided here for protocol completeness
+ with externalStreams - it is normally not used with other
+ streams."
+
+ |max|
+
+ (collection isByteCollection
+ and:[aCollection isByteCollection]) ifTrue:[
+ "do it the fast way"
+ max := (readLimit - position) min: numBytes.
+ aCollection
+ replaceBytesFrom:initialIndex
+ to:(initialIndex + max - 1)
+ with:collection
+ startingAt:position+1.
+ position := position + max.
+ ^ max
+ ].
+ "do it the hard way"
+ ^ super nextBytes:numBytes into:aCollection startingAt:initialIndex
+
+ "
+ |s n buffer|
+
+ buffer := ByteArray new:10.
+
+ s := ReadStream on:#[1 2 3 4 5 6 7 8 9].
+ s next:3.
+ n := s nextBytes:9 into:buffer startingAt:1.
+ Transcript showCR:('n = %1; buffer = <%2>' bindWith:n with:buffer)
+ "
+!
+
upToAll:aCollection
- "read until a subcollection consisisting of the elements in aCollection
- is encountered.
+ "read until a subcollection consisisting of the elements in aCollection is encountered.
Return everything read excluding the elements in aCollection.
The position is left before the collection; i.e. the next
read operations will return those elements.
@@ -567,21 +768,9 @@
which position after the found item. We implement the method
this way for the sake of ST80-compatibility."
- |answerStream element last rslt|
+ "/ in the future, this will have the Squeak semantics, which is upToAllExcluding:
- last := aCollection last.
- answerStream := WriteStream on:(self contentsSpecies new).
- [self atEnd] whileFalse:[
- element := self next.
- answerStream nextPut:element.
- element == last ifTrue:[
- ((rslt := answerStream contents) endsWith:aCollection) ifTrue:[
- self position:(self position - aCollection size).
- ^ rslt copyButLast:aCollection size
- ]
- ].
- ].
- ^ answerStream contents
+ ^ self upToAll_positionBefore:aCollection
"
|s|
@@ -593,6 +782,13 @@
"
|s|
s := ReadStream on:'hello world'.
+ Transcript show:'<'; show:(s upToAll:'wo'); showCR:'>'.
+ Transcript showCR:s atEnd.
+ Transcript show:'<'; show:(s upToAll:'wo'); showCR:'>'.
+ "
+ "
+ |s|
+ s := ReadStream on:'hello world'.
Transcript show:'<'; show:(s upToAll:'xx'); showCR:'>'.
Transcript showCR:s atEnd.
Transcript show:'<'; show:(s upToEnd); showCR:'>'.
@@ -600,6 +796,59 @@
"Modified: / 12.1.1998 / 22:06:42 / cg"
"Created: / 12.1.1998 / 22:07:01 / cg"
+!
+
+upToAll_positionBefore:aCollection
+ "read until a subcollection consisisting of the elements in aCollection is encountered.
+ Return everything read excluding the elements in aCollection.
+ The position is left before the collection; i.e. the next
+ read operations will return those elements.
+ If no such subcollection is encountered, all elements up to the end
+ are read and returned.
+ See also #throughAll: which also reads up to some objects
+ but positions behind it and DOES include it in the returned
+ collection."
+
+ |answerStream element last|
+
+ last := aCollection last.
+ answerStream := WriteStream on:(self contentsSpecies new:100).
+ [(element := self nextOrNil) notNil] whileTrue:[
+ answerStream nextPut:element.
+ (element = last and:[answerStream endsBeforePositionWith:aCollection]) ifTrue:[
+ |backStep|
+ backStep := aCollection size negated.
+ self skip:backStep.
+ answerStream skip:backStep.
+ ^ answerStream contents
+ ].
+ ].
+ ^ answerStream contents
+
+ "
+ |s|
+ s := ReadStream on:'hello world'.
+ Transcript show:'<'; show:(s upToAll_positionBefore:'wo'); showCR:'>'.
+ Transcript showCR:s atEnd.
+ Transcript show:'<'; show:(s upToEnd); showCR:'>'.
+ "
+ "
+ |s|
+ s := ReadStream on:'hello world'.
+ Transcript show:'<'; show:(s upToAll_positionBefore:'wo'); showCR:'>'.
+ Transcript showCR:s atEnd.
+ Transcript show:'<'; show:(s upToAll_positionBefore:'wo'); showCR:'>'.
+ "
+ "
+ |s|
+ s := ReadStream on:'hello world'.
+ Transcript show:'<'; show:(s upToAll_positionBefore:'xx'); showCR:'>'.
+ Transcript showCR:s atEnd.
+ Transcript show:'<'; show:(s upToEnd); showCR:'>'.
+ "
+
+ "Modified: / 12.1.1998 / 22:06:42 / cg"
+ "Created: / 12.1.1998 / 22:07:01 / cg"
! !
!PositionableStream methodsFor:'testing'!
@@ -633,11 +882,11 @@
!PositionableStream class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libbasic/PositionableStream.st,v 1.161 2013-08-18 10:56:02 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/PositionableStream.st,v 1.171 2015-03-12 19:24:30 stefan Exp $'
!
version_CVS
- ^ '$Header: /cvs/stx/stx/libbasic/PositionableStream.st,v 1.161 2013-08-18 10:56:02 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/PositionableStream.st,v 1.171 2015-03-12 19:24:30 stefan Exp $'
! !