27 class encoder(object):
28 def __init__(self, *, backrefs=True):
29 self.backrefs = backrefs
36 return bytes([(sec << 3) | pri])
38 def writetag(self, dst, pri, sec, datum):
39 dst.write(self.enctag(pri, sec))
43 if datum is not None and id(datum) not in self.reftab:
44 self.reftab[id(datum)] = ref
54 while (x > 0) or (b & 0x40) != 0:
62 while x < -1 or (b & 0x40) == 0:
70 def writestr(dst, text):
71 dst.write(text.encode("utf-8"))
75 def writefloat(dst, x):
86 mnt, exp = math.frexp(x)
89 while mnt != int(mnt):
93 buf.extend(encoder.encint(mnt))
94 buf.extend(encoder.encint(exp))
95 dst.write(encoder.encint(len(buf)))
98 def dumpseq(self, dst, seq):
101 dst.write(self.enctag(T_END, 0))
103 def dumpmap(self, dst, val):
104 for k, v in val.items():
107 dst.write(self.enctag(T_END, 0))
109 def dump(self, dst, datum):
110 ref = self.reftab.get(id(datum))
112 dst.write(self.enctag(T_INT, INT_REF))
113 dst.write(self.encint(ref))
116 self.writetag(dst, T_NIL, 0, None)
118 self.writetag(dst, T_NIL, NIL_FALSE, None)
120 self.writetag(dst, T_NIL, NIL_TRUE, None)
121 elif isinstance(datum, int):
122 self.writetag(dst, T_INT, 0, None)
123 dst.write(self.encint(datum))
124 elif isinstance(datum, str):
125 self.writetag(dst, T_STR, 0, datum)
126 self.writestr(dst, datum)
127 elif isinstance(datum, (bytes, bytearray)):
128 self.writetag(dst, T_BIT, 0, datum)
129 dst.write(self.encint(len(datum)))
131 elif isinstance(datum, float):
132 self.writetag(dst, T_BIT, BIT_BFLOAT, datum)
133 self.writefloat(dst, datum)
134 elif isinstance(datum, data.symbol):
136 self.writetag(dst, T_STR, STR_SYM, datum)
137 self.writestr(dst, datum.name)
139 nsref = self.nstab.get(datum.ns)
141 nsref = self.writetag(dst, T_SYM, 0, datum)
143 self.writestr(dst, datum.ns)
144 self.writestr(dst, datum.name)
145 if nsref is not None:
146 self.nstab[datum.ns] = nsref
148 self.writetag(dst, T_SYM, 0, datum)
150 dst.write(self.encint(nsref))
151 self.writestr(dst, datum.name)
152 elif isinstance(datum, list):
153 self.writetag(dst, T_CON, CON_SEQ, datum)
154 self.dumpseq(dst, datum)
155 elif isinstance(datum, set):
156 self.writetag(dst, T_CON, CON_SET, datum)
157 self.dumpseq(dst, datum)
158 elif isinstance(datum, dict):
159 self.writetag(dst, T_CON, CON_MAP, datum)
160 self.dumpmap(dst, datum)
161 elif isinstance(datum, data.obj):
162 self.writetag(dst, T_CON, CON_OBJ, datum)
163 self.dump(dst, getattr(type(datum), "typename", None))
164 self.dumpmap(dst, datum.__dict__)
166 raise ValueError("unsupported object type: " + repr(datum))
168 def dump(dst, datum):
169 encoder().dump(dst, datum)
172 class fmterror(Exception):
175 class eoferror(fmterror):
177 super().__init__("unexpected end-of-data")
179 class referror(fmterror):
181 super().__init__("bad backref")
183 class decoder(object):
201 ret += (b & 0x7f) << p
217 return buf.decode("utf-8")
219 def loadsym(self, fp):
222 nsref = self.loadint(fp)
223 if not 0 <= nsref < len(self.reftab):
224 raise fmterror("illegal namespace ref: " + str(nsref))
225 nssym = self.reftab[nsref]
226 if not isinstance(nssym, data.symbol):
227 raise fmterror("illegal namespace ref: " + str(nsref))
230 ns = self.loadstr(fp)
231 nm = self.loadstr(fp)
232 ret = data.symbol.get(ns, nm)
235 def loadlist(self, fp, buf):
240 buf.append(self.loadtagged(fp, tag))
242 def loadmap(self, fp, buf):
247 key = self.loadtagged(fp, tag)
251 buf[key] = self.loadtagged(fp, tag)
253 def makeobjtype(self, nm):
254 return data.namedtype.make(str(nm), (data.obj, object), {}, typename=nm)
256 def loadobj(self, fp, ref=False):
258 refid = len(self.reftab)
259 self.reftab.append(None)
261 typ = self.namedtypes.get(nm)
263 typ = self.namedtypes[nm] = self.makeobjtype(nm)
266 self.reftab[refid] = ret
268 # print(">", nm, hex(st))
269 ret.__dict__.update(self.loadmap(fp, {}))
270 # print("<", nm, hex(fp.tell()), hex(st))
273 def addref(self, obj):
274 self.reftab.append(obj)
277 def loadtagged(self, fp, tag):
278 pri, sec = (tag & 0x7), (tag & 0xf8) >> 3
280 raise fmterror("unexpected end-tag")
283 idx = self.loadint(fp)
284 if not 0 <= idx < len(self.reftab):
286 # print(idx, self.reftab[idx], hex(fp.tell()))
287 return self.reftab[idx]
288 return self.addref(self.loadint(fp))
290 ret = self.loadstr(fp)
292 return self.addref(data.symbol.get("", ret))
293 return self.addref(ret)
295 ln = self.loadint(fp)
299 if sec == BIT_BFLOAT:
300 buf = io.BytesIO(ret)
301 mnt = self.loadint(buf)
302 exp = self.loadint(buf)
315 ret = math.ldexp(mnt, exp - (mnt.bit_length() - 1))
316 return self.addref(ret)
317 return self.addref(ret)
320 return self.addref(True)
321 elif sec == NIL_FALSE:
322 return self.addref(False)
323 return self.addref(None)
325 return self.addref(self.loadsym(fp))
328 return self.loadmap(fp, self.addref({}))
330 return self.loadobj(fp, ref=True)
332 return self.loadlist(fp, self.addref([]))
334 raise fmterror("unknown primary: " + str(pri))
338 return self.loadtagged(fp, tag)
341 return decoder().load(fp)