Added a distutils script.
[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) | bs & 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 splice(self, ln=-1):
97         buf = self.buf
98         if ln < 0:
99             while True:
100                 ret = self.bk.read()
101                 if ret == b"":
102                     self.buf = bytearray()
103                     return bytes(buf)
104                 buf.extend(ret)
105         else:
106             while len(buf) < ln:
107                 rl = max(ln - len(buf), 1024)
108                 ret = self.bk.read(rl)
109                 if ret == b"":
110                     raise eomerror("unexpected end-of-file")
111                 buf.extend(ret)
112             self.buf = buf[ln:]
113             return bytes(buf[:ln])
114
115     def skip(self, ln):
116         if ln < len(self.buf):
117             self.buf = self.buf[ln:]
118         else:
119             ln -= len(self.buf)
120             self.buf = bytearray()
121             if hasattr(self.bk, "seek"):
122                 self.bk.seek(ln - 1, 1)
123                 if len(self.bk.read(1)) != 1:
124                     raise eomerror("unexpected end-of-file")
125             else:
126                 while ln > 0:
127                     r = self.bk.read(ln)
128                     if r == b"":
129                         raise eomerror("unexpected end-of-file")
130                     ln -= len(r)
131
132     def str(self):
133         buf = self.buf
134         p = 0
135         while True:
136             p2 = buf.find(b'\0', p)
137             if p2 > 0:
138                 self.buf = buf[p2 + 1:]
139                 return str(buf[:p2], "utf-8")
140             ret = self.bk.read(1024)
141             if ret == b"":
142                 if len(buf) == 0:
143                     raise eomerror("unexpected end-of-file")
144                 raise fmterror("no string terminator found")
145             p = len(buf)
146             buf.extend(ret)
147
148     def close(self):
149         self.bk.close()
150
151     def __enter__(self):
152         return self
153
154     def __exit__(self, *excinfo):
155         self.close()
156         return False
157
158 class decbuf(decoder):
159     def __init__(self, data):
160         self.data = data
161         self.offset = 0
162
163     def __len__(self):
164         return len(self.data) - self.offset
165
166     def eom(self):
167         return self.offset >= len(self.data)
168
169     def splice(self, ln=-1):
170         if ln < 0:
171             ret = self.data[self.offset:]
172             self.offset = len(self.data)
173             return ret
174         else:
175             if self.offset + ln > len(self.data):
176                 raise eomerror("out of data to decode")
177             ret = self.data[self.offset:self.offset + ln]
178             self.offset += ln
179             return ret
180
181     def str(self):
182         p = self.data.find(b'\0', self.offset)
183         if p < 0:
184             if self.offset == len(self.data):
185                 raise eomerror("out of data to decode")
186             raise fmterror("no string terminator found")
187         ret = str(self.data[self.offset:p], "utf-8")
188         self.offset = p + 1
189         return str(ret)
190
191 class encoder(object):
192     def enstruct(self, fmt, *args):
193         self.extend(pack(fmt, *args))
194         return self
195
196     def int8(self, val):
197         self.enstruct(">b", val)
198         return self
199     def uint8(self, val):
200         self.enstruct(">B", val)
201         return self
202     def int16(self, val):
203         self.enstruct(">h", val)
204         return self
205     def uint16(self, val):
206         self.enstruct(">H", val)
207         return self
208     def int32(self, val):
209         self.enstruct(">i", val)
210         return self
211     def uint32(self, val):
212         self.enstruct(">I", val)
213         return self
214     def int64(self, val):
215         self.enstruct(">q", val)
216         return self
217     def uint64(self, val):
218         self.enstruct(">Q", val)
219         return self
220     def float32(self, val):
221         self.enstruct(">f", val)
222         return self
223     def float64(self, val):
224         self.enstruct(">d", val)
225         return self
226
227     def str(self, val):
228         if val.find('\0') >= 0:
229             raise ValueError("encoded strings must not contain NULs")
230         self.extend(val.encode("utf-8"))
231         self.extend(b"\0")
232         return self
233
234     def ttol(self, val, term=False):
235         for obj in val:
236             if isinstance(obj, int):
237                 if 0 <= obj < 256:
238                     self.uint8(T_UINT8)
239                     self.uint8(obj)
240                 elif 0 <= obj < 65536:
241                     self.uint8(T_UINT16)
242                     self.uint16(obj)
243                 else:
244                     self.uint8(T_INT)
245                     self.int32(obj)
246             elif isinstance(obj, str):
247                 self.uint8(T_STR)
248                 self.str(obj)
249             elif isinstance(obj, utils.coord):
250                 self.uint8(T_COORD)
251                 self.coord(obj)
252             elif isinstance(obj, utils.color):
253                 self.uint8(T_COLOR)
254                 self.color(obj)
255             elif isinstance(obj, list):
256                 self.uint8(T_TTOL)
257                 self.ttol(obj, True)
258             elif isinstance(obj, float):
259                 self.uint8(T_FLOAT32)
260                 self.float32(obj)
261             elif obj is None:
262                 self.uint8(T_NIL)
263             elif isinstance(obj, collections.ByteString):
264                 self.uint8(T_BYTES)
265                 if len(obj) < 128:
266                     self.uint8(len(obj))
267                 else:
268                     self.uint8(0x80).int32(len(obj))
269                 self.extend(obj)
270             else:
271                 raise ValueError("unexpected type in tto-list: %s" % type(obj))
272         if term:
273             self.uint8(T_END)
274         return self
275
276 class encstream(encoder):
277     def __init__(self, bk):
278         self.bk = bk
279
280     def extend(self, data):
281         self.bk.write(data)
282         return self
283
284     def close(self):
285         self.bk.close()
286
287     def __enter__(self):
288         return self
289
290     def __exit__(self, *excinfo):
291         self.close()
292         return False
293
294 class encbuf(encoder, bytearray):
295     def extend(self, data):
296         bytearray.extend(self, data)
297         return self