PageRenderTime 45ms CodeModel.GetById 14ms app.highlight 23ms RepoModel.GetById 2ms app.codeStats 0ms

/Lib/chunk.py

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