summaryrefslogtreecommitdiff
path: root/portato/eix/libeix.pyx
blob: 2a1c7df656cd4fe61839b759cc3759f31c8ffc69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
class EixError (Exception):
    message = "Unknown error."

    def __str__ (self):
        return self.message

class EndOfFileException (EixError):

    def __init__ (self, filename):
        self.message = "End of file reached though it was not expected: '%s'" % filename

class EixReaderClosed (EixError):
    message = "EixReader is already closed."

class UnsupportedVersionError (EixError):

    def __init__ (self, version):
        self.message = "Version '%s' is not supported." % version

cdef class EixReader (object):
    cdef object file
    cdef char closed
    
    cdef readonly object filename
    cdef readonly object version

    supported_versions = (28, )

    def __init__ (self, filename):
        self.filename = filename
        self.file = open(filename, "r")
        self.closed = 0
        
        self.version = self._get_number()

        if self.version not in self.supported_versions:
            raise UnsupportedVersionError(self.version)

    cdef unsigned long _get_number (self) except *:
        cdef unsigned char n
        cdef short count, i
        cdef unsigned long value
        cdef char* rest
        cdef object orest

        n = self._get_one_byte()

        if n < 0xFF:
            value = n
        else:
            count = 0

            while (n == 0xFF):
                count += 1
                n = self._get_one_byte()

            if n == 0:
                n = 0xFF # 0xFF is encoded as 0xFF 0x00
                count -= 1
            
            value = n << (count*8)

            if count > 0:
                orest = self.get_string(count)
                rest = orest # cast to char*

                for 0 <= i < count:
                    value += (<unsigned char>rest[i]) << ((count - i - 1)*8)

        return value

    cdef unsigned char _get_one_byte (self) except? 0:
        s = self.file.read(1)

        if len(s) != 1:
            raise EndOfFileException, self.filename
        
        return ord(s)

    cdef object _get_string (self, length):
        if self.closed:
            raise EixReaderClosed

        s = self.file.read(length)

        if len(s) != length:
            raise EndOfFileException, self.filename

        return s

    def get_number (self):
        if self.closed:
            raise EixReaderClosed
        
        return self._get_number()

    def close (self):
        if self.closed:
            raise EixReaderClosed
        
        self.file.close()
        self.closed = 1