Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

streaming.py 6.5KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #
  2. # This file is part of pyasn1 software.
  3. #
  4. # Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
  5. # License: https://pyasn1.readthedocs.io/en/latest/license.html
  6. #
  7. import io
  8. import os
  9. import sys
  10. from pyasn1 import error
  11. from pyasn1.type import univ
  12. _PY2 = sys.version_info < (3,)
  13. class CachingStreamWrapper(io.IOBase):
  14. """Wrapper around non-seekable streams.
  15. Note that the implementation is tied to the decoder,
  16. not checking for dangerous arguments for the sake
  17. of performance.
  18. The read bytes are kept in an internal cache until
  19. setting _markedPosition which may reset the cache.
  20. """
  21. def __init__(self, raw):
  22. self._raw = raw
  23. self._cache = io.BytesIO()
  24. self._markedPosition = 0
  25. def peek(self, n):
  26. result = self.read(n)
  27. self._cache.seek(-len(result), os.SEEK_CUR)
  28. return result
  29. def seekable(self):
  30. return True
  31. def seek(self, n=-1, whence=os.SEEK_SET):
  32. # Note that this not safe for seeking forward.
  33. return self._cache.seek(n, whence)
  34. def read(self, n=-1):
  35. read_from_cache = self._cache.read(n)
  36. if n != -1:
  37. n -= len(read_from_cache)
  38. if not n: # 0 bytes left to read
  39. return read_from_cache
  40. read_from_raw = self._raw.read(n)
  41. self._cache.write(read_from_raw)
  42. return read_from_cache + read_from_raw
  43. @property
  44. def markedPosition(self):
  45. """Position where the currently processed element starts.
  46. This is used for back-tracking in SingleItemDecoder.__call__
  47. and (indefLen)ValueDecoder and should not be used for other purposes.
  48. The client is not supposed to ever seek before this position.
  49. """
  50. return self._markedPosition
  51. @markedPosition.setter
  52. def markedPosition(self, value):
  53. # By setting the value, we ensure we won't seek back before it.
  54. # `value` should be the same as the current position
  55. # We don't check for this for performance reasons.
  56. self._markedPosition = value
  57. # Whenever we set _marked_position, we know for sure
  58. # that we will not return back, and thus it is
  59. # safe to drop all cached data.
  60. if self._cache.tell() > io.DEFAULT_BUFFER_SIZE:
  61. self._cache = io.BytesIO(self._cache.read())
  62. self._markedPosition = 0
  63. def tell(self):
  64. return self._cache.tell()
  65. def asSeekableStream(substrate):
  66. """Convert object to seekable byte-stream.
  67. Parameters
  68. ----------
  69. substrate: :py:class:`bytes` or :py:class:`io.IOBase` or :py:class:`univ.OctetString`
  70. Returns
  71. -------
  72. : :py:class:`io.IOBase`
  73. Raises
  74. ------
  75. : :py:class:`~pyasn1.error.PyAsn1Error`
  76. If the supplied substrate cannot be converted to a seekable stream.
  77. """
  78. if isinstance(substrate, io.BytesIO):
  79. return substrate
  80. elif isinstance(substrate, bytes):
  81. return io.BytesIO(substrate)
  82. elif isinstance(substrate, univ.OctetString):
  83. return io.BytesIO(substrate.asOctets())
  84. try:
  85. # Special case: impossible to set attributes on `file` built-in
  86. # XXX: broken, BufferedReader expects a "readable" attribute.
  87. if _PY2 and isinstance(substrate, file):
  88. return io.BufferedReader(substrate)
  89. elif substrate.seekable(): # Will fail for most invalid types
  90. return substrate
  91. else:
  92. return CachingStreamWrapper(substrate)
  93. except AttributeError:
  94. raise error.UnsupportedSubstrateError(
  95. "Cannot convert " + substrate.__class__.__name__ +
  96. " to a seekable bit stream.")
  97. def isEndOfStream(substrate):
  98. """Check whether we have reached the end of a stream.
  99. Although it is more effective to read and catch exceptions, this
  100. function
  101. Parameters
  102. ----------
  103. substrate: :py:class:`IOBase`
  104. Stream to check
  105. Returns
  106. -------
  107. : :py:class:`bool`
  108. """
  109. if isinstance(substrate, io.BytesIO):
  110. cp = substrate.tell()
  111. substrate.seek(0, os.SEEK_END)
  112. result = substrate.tell() == cp
  113. substrate.seek(cp, os.SEEK_SET)
  114. yield result
  115. else:
  116. received = substrate.read(1)
  117. if received is None:
  118. yield
  119. if received:
  120. substrate.seek(-1, os.SEEK_CUR)
  121. yield not received
  122. def peekIntoStream(substrate, size=-1):
  123. """Peek into stream.
  124. Parameters
  125. ----------
  126. substrate: :py:class:`IOBase`
  127. Stream to read from.
  128. size: :py:class:`int`
  129. How many bytes to peek (-1 = all available)
  130. Returns
  131. -------
  132. : :py:class:`bytes` or :py:class:`str`
  133. The return type depends on Python major version
  134. """
  135. if hasattr(substrate, "peek"):
  136. received = substrate.peek(size)
  137. if received is None:
  138. yield
  139. while len(received) < size:
  140. yield
  141. yield received
  142. else:
  143. current_position = substrate.tell()
  144. try:
  145. for chunk in readFromStream(substrate, size):
  146. yield chunk
  147. finally:
  148. substrate.seek(current_position)
  149. def readFromStream(substrate, size=-1, context=None):
  150. """Read from the stream.
  151. Parameters
  152. ----------
  153. substrate: :py:class:`IOBase`
  154. Stream to read from.
  155. Keyword parameters
  156. ------------------
  157. size: :py:class:`int`
  158. How many bytes to read (-1 = all available)
  159. context: :py:class:`dict`
  160. Opaque caller context will be attached to exception objects created
  161. by this function.
  162. Yields
  163. ------
  164. : :py:class:`bytes` or :py:class:`str` or :py:class:`SubstrateUnderrunError`
  165. Read data or :py:class:`~pyasn1.error.SubstrateUnderrunError`
  166. object if no `size` bytes is readily available in the stream. The
  167. data type depends on Python major version
  168. Raises
  169. ------
  170. : :py:class:`~pyasn1.error.EndOfStreamError`
  171. Input stream is exhausted
  172. """
  173. while True:
  174. # this will block unless stream is non-blocking
  175. received = substrate.read(size)
  176. if received is None: # non-blocking stream can do this
  177. yield error.SubstrateUnderrunError(context=context)
  178. elif not received and size != 0: # end-of-stream
  179. raise error.EndOfStreamError(context=context)
  180. elif len(received) < size:
  181. substrate.seek(-len(received), os.SEEK_CUR)
  182. # behave like a non-blocking stream
  183. yield error.SubstrateUnderrunError(context=context)
  184. else:
  185. break
  186. yield received