(root)/
Python-3.12.0/
Lib/
chunk.py
       1  """Simple class to read IFF chunks.
       2  
       3  An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File
       4  Format)) has the following structure:
       5  
       6  +----------------+
       7  | ID (4 bytes)   |
       8  +----------------+
       9  | size (4 bytes) |
      10  +----------------+
      11  | data           |
      12  | ...            |
      13  +----------------+
      14  
      15  The ID is a 4-byte string which identifies the type of chunk.
      16  
      17  The size field (a 32-bit value, encoded using big-endian byte order)
      18  gives the size of the whole chunk, including the 8-byte header.
      19  
      20  Usually an IFF-type file consists of one or more chunks.  The proposed
      21  usage of the Chunk class defined here is to instantiate an instance at
      22  the start of each chunk and read from the instance until it reaches
      23  the end, after which a new instance can be instantiated.  At the end
      24  of the file, creating a new instance will fail with an EOFError
      25  exception.
      26  
      27  Usage:
      28  while True:
      29      try:
      30          chunk = Chunk(file)
      31      except EOFError:
      32          break
      33      chunktype = chunk.getname()
      34      while True:
      35          data = chunk.read(nbytes)
      36          if not data:
      37              pass
      38          # do something with data
      39  
      40  The interface is file-like.  The implemented methods are:
      41  read, close, seek, tell, isatty.
      42  Extra methods are: skip() (called by close, skips to the end of the chunk),
      43  getname() (returns the name (ID) of the chunk)
      44  
      45  The __init__ method has one required argument, a file-like object
      46  (including a chunk instance), and one optional argument, a flag which
      47  specifies whether or not chunks are aligned on 2-byte boundaries.  The
      48  default is 1, i.e. aligned.
      49  """
      50  
      51  import warnings
      52  
      53  warnings._deprecated(__name__, remove=(3, 13))
      54  
      55  class ESC[4;38;5;81mChunk:
      56      def __init__(self, file, align=True, bigendian=True, inclheader=False):
      57          import struct
      58          self.closed = False
      59          self.align = align      # whether to align to word (2-byte) boundaries
      60          if bigendian:
      61              strflag = '>'
      62          else:
      63              strflag = '<'
      64          self.file = file
      65          self.chunkname = file.read(4)
      66          if len(self.chunkname) < 4:
      67              raise EOFError
      68          try:
      69              self.chunksize = struct.unpack_from(strflag+'L', file.read(4))[0]
      70          except struct.error:
      71              raise EOFError from None
      72          if inclheader:
      73              self.chunksize = self.chunksize - 8 # subtract header
      74          self.size_read = 0
      75          try:
      76              self.offset = self.file.tell()
      77          except (AttributeError, OSError):
      78              self.seekable = False
      79          else:
      80              self.seekable = True
      81  
      82      def getname(self):
      83          """Return the name (ID) of the current chunk."""
      84          return self.chunkname
      85  
      86      def getsize(self):
      87          """Return the size of the current chunk."""
      88          return self.chunksize
      89  
      90      def close(self):
      91          if not self.closed:
      92              try:
      93                  self.skip()
      94              finally:
      95                  self.closed = True
      96  
      97      def isatty(self):
      98          if self.closed:
      99              raise ValueError("I/O operation on closed file")
     100          return False
     101  
     102      def seek(self, pos, whence=0):
     103          """Seek to specified position into the chunk.
     104          Default position is 0 (start of chunk).
     105          If the file is not seekable, this will result in an error.
     106          """
     107  
     108          if self.closed:
     109              raise ValueError("I/O operation on closed file")
     110          if not self.seekable:
     111              raise OSError("cannot seek")
     112          if whence == 1:
     113              pos = pos + self.size_read
     114          elif whence == 2:
     115              pos = pos + self.chunksize
     116          if pos < 0 or pos > self.chunksize:
     117              raise RuntimeError
     118          self.file.seek(self.offset + pos, 0)
     119          self.size_read = pos
     120  
     121      def tell(self):
     122          if self.closed:
     123              raise ValueError("I/O operation on closed file")
     124          return self.size_read
     125  
     126      def read(self, size=-1):
     127          """Read at most size bytes from the chunk.
     128          If size is omitted or negative, read until the end
     129          of the chunk.
     130          """
     131  
     132          if self.closed:
     133              raise ValueError("I/O operation on closed file")
     134          if self.size_read >= self.chunksize:
     135              return b''
     136          if size < 0:
     137              size = self.chunksize - self.size_read
     138          if size > self.chunksize - self.size_read:
     139              size = self.chunksize - self.size_read
     140          data = self.file.read(size)
     141          self.size_read = self.size_read + len(data)
     142          if self.size_read == self.chunksize and \
     143             self.align and \
     144             (self.chunksize & 1):
     145              dummy = self.file.read(1)
     146              self.size_read = self.size_read + len(dummy)
     147          return data
     148  
     149      def skip(self):
     150          """Skip the rest of the chunk.
     151          If you are not interested in the contents of the chunk,
     152          this method should be called so that the file points to
     153          the start of the next chunk.
     154          """
     155  
     156          if self.closed:
     157              raise ValueError("I/O operation on closed file")
     158          if self.seekable:
     159              try:
     160                  n = self.chunksize - self.size_read
     161                  # maybe fix alignment
     162                  if self.align and (self.chunksize & 1):
     163                      n = n + 1
     164                  self.file.seek(n, 1)
     165                  self.size_read = self.size_read + n
     166                  return
     167              except OSError:
     168                  pass
     169          while self.size_read < self.chunksize:
     170              n = min(8192, self.chunksize - self.size_read)
     171              dummy = self.read(n)
     172              if not dummy:
     173                  raise EOFError