Add a class-loading module.
[pycfml.git] / classfile / binfmt.py
1 from struct import pack, unpack, calcsize
2
3 class fmterror(Exception):
4     pass
5
6 class eomerror(fmterror):
7     pass
8
9 def mutf8dec(bs):
10     ret = ""
11     i = 0
12     while i < len(bs):
13         b = bs[i]
14         i += 1
15         if b & 0x80 == 0:
16             ret += chr(b)
17         else:
18             c = 0
19             while (c < 7) and (b & (1 << (6 - c))):
20                 c += 1
21             if c == 0 or c == 7: raise fmterror("invalid utf8 start-byte")
22             iacc = acc = b & ((1 << (6 - c)) - 1)
23             ic = c
24             while c > 0:
25                 if i >= len(bs): raise fmterror("unterminated utf8 compound")
26                 b = bs[i]
27                 i += 1
28                 if b & 0xc0 != 0x80: raise fmterror("invalid utf8 continuation byte")
29                 acc = (acc << 6) | b & 0x3f
30                 c -= 1
31             if iacc == 0 and ic != 2 and acc != 0: raise fmterror("invalid utf8 compound")
32             ret += chr(acc)
33     return ret
34
35 def mutf8enc(cs):
36     ret = bytearray()
37     for c in cs:
38         c = ord(c)
39         if c == 0:
40             ret.extend(b"\xc0\x80")
41         elif 1 <= c < 128:
42             ret.append(c)
43         elif 128 <= c < 2048:
44             ret.append(0xc0 | ((c & 0x7c0) >> 6))
45             ret.append(0x80 |  (c & 0x03f))
46         elif 2048 <= c < 65536:
47             ret.append(0xe0 | ((c & 0xf000) >> 12))
48             ret.append(0x80 | ((c & 0x0fc0) >> 6))
49             ret.append(0x80 |  (c & 0x003f))
50         else:
51             raise fmterror("non-BMP unicode not supported by Java")
52     return bytes(ret)
53
54 class decoder(object):
55     def destruct(self, fmt):
56         return unpack(fmt, self.splice(calcsize(fmt)))
57
58     def skip(self, ln):
59         self.splice(ln)
60
61     def int8(self):
62         return self.destruct(">b")[0]
63     def uint8(self):
64         return self.destruct(">B")[0]
65     def int16(self):
66         return self.destruct(">h")[0]
67     def uint16(self):
68         return self.destruct(">H")[0]
69     def int32(self):
70         return self.destruct(">i")[0]
71     def uint32(self):
72         return self.destruct(">I")[0]
73     def int64(self):
74         return self.destruct(">q")[0]
75     def uint64(self):
76         return self.destruct(">Q")[0]
77     def float32(self):
78         return self.destruct(">f")[0]
79     def float64(self):
80         return self.destruct(">d")[0]
81
82 class decstream(decoder):
83     def __init__(self, bk):
84         self.bk = bk
85         self.buf = bytearray()
86
87     def eom(self):
88         if len(self.buf) > 0:
89             return False
90         ret = self.bk.read(1024)
91         if ret == b"":
92             return True
93         self.buf.extend(ret)
94         return False
95
96     def tell(self):
97         return self.bk.tell() - len(self.buf)
98
99     def splice(self, ln=-1):
100         buf = self.buf
101         if ln < 0:
102             while True:
103                 ret = self.bk.read()
104                 if ret == b"":
105                     self.buf = bytearray()
106                     return bytes(buf)
107                 buf.extend(ret)
108         else:
109             while len(buf) < ln:
110                 rl = max(ln - len(buf), 1024)
111                 ret = self.bk.read(rl)
112                 if ret == b"":
113                     raise eomerror("unexpected end-of-file")
114                 buf.extend(ret)
115             self.buf = buf[ln:]
116             return bytes(buf[:ln])
117
118     def skip(self, ln):
119         if ln < len(self.buf):
120             self.buf = self.buf[ln:]
121         else:
122             ln -= len(self.buf)
123             self.buf = bytearray()
124             if hasattr(self.bk, "seek"):
125                 self.bk.seek(ln - 1, 1)
126                 if len(self.bk.read(1)) != 1:
127                     raise eomerror("unexpected end-of-file")
128             else:
129                 while ln > 0:
130                     r = self.bk.read(ln)
131                     if r == b"":
132                         raise eomerror("unexpected end-of-file")
133                     ln -= len(r)
134
135     def str(self):
136         buf = self.buf
137         p = 0
138         while True:
139             p2 = buf.find(b'\0', p)
140             if p2 > 0:
141                 self.buf = buf[p2 + 1:]
142                 return str(buf[:p2], "utf-8")
143             ret = self.bk.read(1024)
144             if ret == b"":
145                 if len(buf) == 0:
146                     raise eomerror("unexpected end-of-file")
147                 raise fmterror("no string terminator found")
148             p = len(buf)
149             buf.extend(ret)
150
151     def close(self):
152         self.bk.close()
153
154     def __enter__(self):
155         return self
156
157     def __exit__(self, *excinfo):
158         self.close()
159         return False
160
161 class decbuf(decoder):
162     def __init__(self, data):
163         self.data = data
164         self.offset = 0
165
166     def __len__(self):
167         return len(self.data) - self.offset
168
169     def eom(self):
170         return self.offset >= len(self.data)
171
172     def splice(self, ln=-1):
173         if ln < 0:
174             ret = self.data[self.offset:]
175             self.offset = len(self.data)
176             return ret
177         else:
178             if self.offset + ln > len(self.data):
179                 raise eomerror("out of data to decode")
180             ret = self.data[self.offset:self.offset + ln]
181             self.offset += ln
182             return ret
183
184     def str(self):
185         p = self.data.find(b'\0', self.offset)
186         if p < 0:
187             if self.offset == len(self.data):
188                 raise eomerror("out of data to decode")
189             raise fmterror("no string terminator found")
190         ret = str(self.data[self.offset:p], "utf-8")
191         self.offset = p + 1
192         return str(ret)
193
194 class encoder(object):
195     def enstruct(self, fmt, *args):
196         self.extend(pack(fmt, *args))
197         return self
198
199     def int8(self, val):
200         self.enstruct(">b", val)
201         return self
202     def uint8(self, val):
203         self.enstruct(">B", val)
204         return self
205     def int16(self, val):
206         self.enstruct(">h", val)
207         return self
208     def uint16(self, val):
209         self.enstruct(">H", val)
210         return self
211     def int32(self, val):
212         self.enstruct(">i", val)
213         return self
214     def uint32(self, val):
215         self.enstruct(">I", val)
216         return self
217     def int64(self, val):
218         self.enstruct(">q", val)
219         return self
220     def uint64(self, val):
221         self.enstruct(">Q", val)
222         return self
223     def float32(self, val):
224         self.enstruct(">f", val)
225         return self
226     def float64(self, val):
227         self.enstruct(">d", val)
228         return self
229
230     def str(self, val):
231         if val.find('\0') >= 0:
232             raise ValueError("encoded strings must not contain NULs")
233         self.extend(val.encode("utf-8"))
234         self.extend(b"\0")
235         return self
236
237     def ttol(self, val, term=False):
238         for obj in val:
239             if isinstance(obj, int):
240                 if 0 <= obj < 256:
241                     self.uint8(T_UINT8)
242                     self.uint8(obj)
243                 elif 0 <= obj < 65536:
244                     self.uint8(T_UINT16)
245                     self.uint16(obj)
246                 else:
247                     self.uint8(T_INT)
248                     self.int32(obj)
249             elif isinstance(obj, str):
250                 self.uint8(T_STR)
251                 self.str(obj)
252             elif isinstance(obj, utils.coord):
253                 self.uint8(T_COORD)
254                 self.coord(obj)
255             elif isinstance(obj, utils.color):
256                 self.uint8(T_COLOR)
257                 self.color(obj)
258             elif isinstance(obj, list):
259                 self.uint8(T_TTOL)
260                 self.ttol(obj, True)
261             elif isinstance(obj, float):
262                 self.uint8(T_FLOAT32)
263                 self.float32(obj)
264             elif obj is None:
265                 self.uint8(T_NIL)
266             elif isinstance(obj, collections.ByteString):
267                 self.uint8(T_BYTES)
268                 if len(obj) < 128:
269                     self.uint8(len(obj))
270                 else:
271                     self.uint8(0x80).int32(len(obj))
272                 self.extend(obj)
273             else:
274                 raise ValueError("unexpected type in tto-list: %s" % type(obj))
275         if term:
276             self.uint8(T_END)
277         return self
278
279 class encstream(encoder):
280     def __init__(self, bk):
281         self.bk = bk
282
283     def extend(self, data):
284         self.bk.write(data)
285         return self
286
287     def close(self):
288         self.bk.close()
289
290     def __enter__(self):
291         return self
292
293     def __exit__(self, *excinfo):
294         self.close()
295         return False
296
297 class encbuf(encoder, bytearray):
298     def extend(self, data):
299         bytearray.extend(self, data)
300         return self