PositionableStream.st
branchjv
changeset 18120 e3a375d5f6a8
parent 18084 ab5b38bd8f81
parent 17611 a3d9a43a6197
child 18260 e55af242e134
--- 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 $'
 ! !