1  package frost.io
  2  
  3  ====================================================================================================
  4  An input stream which allows data to be "pushed back" into it, so that it may be read again. This is
  5  useful in situations such as parsing, in which you may wish to read until you encounter a delimiter
  6  and then push the delimiter back into the stream so that it can be handled by other code.
  7  
  8  The `PushbackInputStream` "sits on top of" another stream, reading from it when it requires new
  9  data. You should not read directly from the underlying stream while reading from a
 10  `PushbackInputStream`. Closing the `PushbackInputStream` *does not* close the underlying stream, but
 11  if the `PushbackInputStream` holds the only remaining reference to the underlying stream, then
 12  discarding the `PushbackInputStream` will cause the underlying stream to likewise be discarded and
 13  thus closed.
 14  ====================================================================================================
 15  class PushbackInputStream : InputStream {
 16      @private
 17      def source:InputStream
 18  
 19      @private
 20      def pushbackBuffer := Array<UInt8>()
 21  
 22      ================================================================================================
 23      Creates a new `PushbackInputStream`.
 24  
 25      @param raw the underlying stream
 26      ================================================================================================
 27      init(source:InputStream) {
 28          self.source := source
 29          super.init()
 30      }
 31  
 32      @override
 33      method read():UInt8? {
 34          if pushbackBuffer.count > 0 {
 35              def result := pushbackBuffer[pushbackBuffer.count - 1]
 36              pushbackBuffer.removeIndex(pushbackBuffer.count - 1)
 37              return result
 38          }
 39          return source.read()
 40      }
 41  
 42      ============================================================================
 43      Pushes back a single `UInt8`, so that it will be the next byte read by the
 44      stream.
 45  
 46      @param v the `UInt8` to push
 47      ============================================================================
 48      method pushback(v:UInt8) {
 49          pushbackBuffer.add(v)
 50      }
 51  
 52      ============================================================================
 53      Pushes back a single `Int8`, so that it will be the next byte read by the
 54      stream.
 55  
 56      @param v the `Int8` to push
 57      ============================================================================
 58      method pushback(v:Int8) {
 59          pushbackBuffer.add(v.asUInt8)
 60      }
 61  
 62      ============================================================================
 63      Pushes back a list of `UInt8`s.
 64  
 65      @param v the array to push
 66      ============================================================================
 67      method pushback(v:ListView<UInt8>) {
 68          for i in v.count - 1 ... 0 by -1 {
 69              pushback(v[i])
 70          }
 71      }
 72  
 73      ============================================================================
 74      Pushes back a list of `Int8`s.
 75  
 76      @param v the array to push
 77      ============================================================================
 78      method pushback(v:ListView<Int8>) {
 79          for i in v.count - 1 ... 0 by -1 {
 80              pushback(v[i])
 81          }
 82      }
 83  
 84      ============================================================================
 85      Pushes back a single `Char8`.
 86  
 87      @param c the character to push
 88      ============================================================================
 89      method pushback(c:Char8) {
 90          pushback(c.asUInt8)
 91      }
 92  
 93      ============================================================================
 94      Pushes back a list of `Char`s.
 95  
 96      @param c the array to push
 97      ============================================================================
 98      method pushback(c:ListView<Char8>) {
 99          for i in c.count - 1 ... 0 by -1 {
100              pushback(c[i])
101          }
102      }
103  
104      ============================================================================
105      Pushes back a `String`.
106  
107      @param s the string to push
108      ============================================================================
109      method pushback(s:String) {
110          pushback(s.utf8)
111      }
112  }