1#
2# Copyright (C) 2016 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16"""Generic parser class for reading GCNO and GCDA files.
17
18Implements read functions for strings, 32-bit integers, and
1964-bit integers.
20"""
21
22import struct
23
24
25class FileFormatError(Exception):
26    """Exception for invalid file format.
27
28    Thrown when an unexpected value type is read from the file stream
29    or when the end of file is reached unexpectedly."""
30
31    pass
32
33
34class GcovStreamParserUtil(object):
35    """Parser object for storing the stream and format information.
36
37    Attributes:
38        stream: File stream object for a GCNO file
39        format: Character denoting the endianness of the file
40        checksum: The checksum (int) of the file
41    """
42
43    def __init__(self, stream, magic):
44        """Inits the parser with the input stream.
45
46        The byte order is set by default to little endian and the summary file
47        is instantiated with an empty GCNOSummary object.
48
49        Args:
50            stream: An input binary file stream to a .gcno file
51            gcno_summary: The summary from a parsed gcno file
52        """
53        self.stream = stream
54        self.format = '<'
55
56        tag = self.ReadInt()
57        self.version = ''.join(
58            struct.unpack(self.format + 'ssss', self.stream.read(4)))
59        self.checksum = self.ReadInt()
60
61        if tag != magic:
62            tag = struct.unpack('>I', struct.pack('<I', tag))[0]
63            if tag == magic:  #  switch endianness
64                self.format = '>'
65            else:
66                raise FileFormatError('Invalid file format.')
67
68    def ReadInt(self):
69        """Reads and returns an integer from the stream.
70
71        Returns:
72          A 4-byte integer from the stream attribute.
73
74        Raises:
75          FileFormatError: Corrupt file.
76        """
77        try:
78            return struct.unpack(self.format + 'I', self.stream.read(4))[0]
79        except (TypeError, ValueError, struct.error) as error:
80            raise FileFormatError('Corrupt file.')
81
82    def ReadInt64(self):
83        """Reads and returns a 64-bit integer from the stream.
84
85        Returns:
86            An 8-byte integer from the stream attribute.
87
88        Raises:
89            FileFormatError: Corrupt file.
90        """
91        lo = self.ReadInt()
92        hi = self.ReadInt()
93        return (hi << 32) | lo
94
95    def ReadString(self):
96        """Reads and returns a string from the stream.
97
98        First reads an integer denoting the number of words to read,
99        then reads and returns the string with trailing padding characters
100        stripped.
101
102        Returns:
103            A string from the stream attribute.
104
105        Raises:
106            FileFormatError: End of file reached.
107        """
108        length = self.ReadInt() << 2
109        if length > 0:
110            try:
111                return ''.join(
112                    struct.unpack(self.format + 's' * length, self.stream.read(
113                        length))).rstrip('\x00')
114            except (TypeError, ValueError, struct.error):
115                raise FileFormatError('Corrupt file.')
116        return str()
117