brine.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. """
  2. brine - a simple, fast and secure object serializer for immutable objects,
  3. optimized for small integers [-48..160).
  4. the following types are supported: int, long, bool, str, float, unicode,
  5. slice, complex, tuple(of simple types), forzenset(of simple types)
  6. as well as the following singletons: None, NotImplemented, Ellipsis
  7. """
  8. from cStringIO import StringIO
  9. from rpyc.lib.compat import Struct, all
  10. # singletons
  11. TAG_NONE = "\x00"
  12. TAG_EMPTY_STR = "\x01"
  13. TAG_EMPTY_TUPLE = "\x02"
  14. TAG_TRUE = "\x03"
  15. TAG_FALSE = "\x04"
  16. TAG_NOT_IMPLEMENTED = "\x05"
  17. TAG_ELLIPSIS = "\x06"
  18. # types
  19. TAG_UNICODE = "\x08"
  20. TAG_LONG = "\x09"
  21. TAG_STR1 = "\x0a"
  22. TAG_STR2 = "\x0b"
  23. TAG_STR3 = "\x0c"
  24. TAG_STR4 = "\x0d"
  25. TAG_STR_L1 = "\x0e"
  26. TAG_STR_L4 = "\x0f"
  27. TAG_TUP1 = "\x10"
  28. TAG_TUP2 = "\x11"
  29. TAG_TUP3 = "\x12"
  30. TAG_TUP4 = "\x13"
  31. TAG_TUP_L1 = "\x14"
  32. TAG_TUP_L4 = "\x15"
  33. TAG_INT_L1 = "\x16"
  34. TAG_INT_L4 = "\x17"
  35. TAG_FLOAT = "\x18"
  36. TAG_SLICE = "\x19"
  37. TAG_FSET = "\x1a"
  38. TAG_COMPLEX = "\x1b"
  39. IMM_INTS = dict((i, chr(i + 0x50)) for i in range(-0x30, 0xa0))
  40. I1 = Struct("!B")
  41. I4 = Struct("!L")
  42. F8 = Struct("!d")
  43. C16 = Struct("!dd")
  44. _dump_registry = {}
  45. _load_registry = {}
  46. IMM_INTS_LOADER = dict((v, k) for k, v in IMM_INTS.iteritems())
  47. def register(coll, key):
  48. def deco(func):
  49. coll[key] = func
  50. return func
  51. return deco
  52. #===============================================================================
  53. # dumping
  54. #===============================================================================
  55. @register(_dump_registry, type(None))
  56. def _dump_none(obj, stream):
  57. stream.append(TAG_NONE)
  58. @register(_dump_registry, type(NotImplemented))
  59. def _dump_notimplemeted(obj, stream):
  60. stream.append(TAG_NOT_IMPLEMENTED)
  61. @register(_dump_registry, type(Ellipsis))
  62. def _dump_ellipsis(obj, stream):
  63. stream.append(TAG_ELLIPSIS)
  64. @register(_dump_registry, bool)
  65. def _dump_bool(obj, stream):
  66. if obj:
  67. stream.append(TAG_TRUE)
  68. else:
  69. stream.append(TAG_FALSE)
  70. @register(_dump_registry, slice)
  71. def _dump_slice(obj, stream):
  72. stream.append(TAG_SLICE)
  73. _dump((obj.start, obj.stop, obj.step), stream)
  74. @register(_dump_registry, frozenset)
  75. def _dump_frozenset(obj, stream):
  76. stream.append(TAG_FSET)
  77. _dump(tuple(obj), stream)
  78. @register(_dump_registry, int)
  79. def _dump_int(obj, stream):
  80. if obj in IMM_INTS:
  81. stream.append(IMM_INTS[obj])
  82. else:
  83. obj = str(obj)
  84. l = len(obj)
  85. if l < 256:
  86. stream.append(TAG_INT_L1 + I1.pack(l) + obj)
  87. else:
  88. stream.append(TAG_INT_L4 + I4.pack(l) + obj)
  89. @register(_dump_registry, long)
  90. def _dump_long(obj, stream):
  91. stream.append(TAG_LONG)
  92. _dump_int(obj, stream)
  93. @register(_dump_registry, str)
  94. def _dump_str(obj, stream):
  95. l = len(obj)
  96. if l == 0:
  97. stream.append(TAG_EMPTY_STR)
  98. elif l == 1:
  99. stream.append(TAG_STR1 + obj)
  100. elif l == 2:
  101. stream.append(TAG_STR2 + obj)
  102. elif l == 3:
  103. stream.append(TAG_STR3 + obj)
  104. elif l == 4:
  105. stream.append(TAG_STR4 + obj)
  106. elif l < 256:
  107. stream.append(TAG_STR_L1 + I1.pack(l) + obj)
  108. else:
  109. stream.append(TAG_STR_L4 + I4.pack(l) + obj)
  110. @register(_dump_registry, float)
  111. def _dump_float(obj, stream):
  112. stream.append(TAG_FLOAT + F8.pack(obj))
  113. @register(_dump_registry, complex)
  114. def _dump_complex(obj, stream):
  115. stream.append(TAG_COMPLEX + C16.pack(obj.real, obj.imag))
  116. @register(_dump_registry, unicode)
  117. def _dump_unicode(obj, stream):
  118. stream.append(TAG_UNICODE)
  119. _dump_str(obj.encode("utf8"), stream)
  120. @register(_dump_registry, tuple)
  121. def _dump_tuple(obj, stream):
  122. l = len(obj)
  123. if l == 0:
  124. stream.append(TAG_EMPTY_TUPLE)
  125. elif l == 1:
  126. stream.append(TAG_TUP1)
  127. elif l == 2:
  128. stream.append(TAG_TUP2)
  129. elif l == 3:
  130. stream.append(TAG_TUP3)
  131. elif l == 4:
  132. stream.append(TAG_TUP4)
  133. elif l < 256:
  134. stream.append(TAG_TUP_L1 + I1.pack(l))
  135. else:
  136. stream.append(TAG_TUP_L4 + I4.pack(l))
  137. for item in obj:
  138. _dump(item, stream)
  139. def _undumpable(obj, stream):
  140. raise TypeError("cannot dump %r" % (obj,))
  141. def _dump(obj, stream):
  142. _dump_registry.get(type(obj), _undumpable)(obj, stream)
  143. #===============================================================================
  144. # loading
  145. #===============================================================================
  146. @register(_load_registry, TAG_NONE)
  147. def _load_none(stream):
  148. return None
  149. @register(_load_registry, TAG_NOT_IMPLEMENTED)
  150. def _load_nonimp(stream):
  151. return NotImplemented
  152. @register(_load_registry, TAG_ELLIPSIS)
  153. def _load_elipsis(stream):
  154. return Ellipsis
  155. @register(_load_registry, TAG_TRUE)
  156. def _load_true(stream):
  157. return True
  158. @register(_load_registry, TAG_FALSE)
  159. def _load_false(stream):
  160. return False
  161. @register(_load_registry, TAG_EMPTY_TUPLE)
  162. def _load_empty_tuple(stream):
  163. return ()
  164. @register(_load_registry, TAG_EMPTY_STR)
  165. def _load_empty_str(stream):
  166. return ""
  167. @register(_load_registry, TAG_UNICODE)
  168. def _load_unicode(stream):
  169. obj = _load(stream)
  170. return obj.decode("utf-8")
  171. @register(_load_registry, TAG_LONG)
  172. def _load_long(stream):
  173. obj = _load(stream)
  174. return long(obj)
  175. @register(_load_registry, TAG_FLOAT)
  176. def _load_float(stream):
  177. return F8.unpack(stream.read(8))[0]
  178. @register(_load_registry, TAG_COMPLEX)
  179. def _load_complex(stream):
  180. real, imag = C16.unpack(stream.read(16))
  181. return complex(real, imag)
  182. @register(_load_registry, TAG_STR1)
  183. def _load_str1(stream):
  184. return stream.read(1)
  185. @register(_load_registry, TAG_STR2)
  186. def _load_str2(stream):
  187. return stream.read(2)
  188. @register(_load_registry, TAG_STR3)
  189. def _load_str3(stream):
  190. return stream.read(3)
  191. @register(_load_registry, TAG_STR4)
  192. def _load_str4(stream):
  193. return stream.read(4)
  194. @register(_load_registry, TAG_STR_L1)
  195. def _load_str_l1(stream):
  196. l, = I1.unpack(stream.read(1))
  197. return stream.read(l)
  198. @register(_load_registry, TAG_STR_L4)
  199. def _load_str_l4(stream):
  200. l, = I4.unpack(stream.read(4))
  201. return stream.read(l)
  202. @register(_load_registry, TAG_TUP1)
  203. def _load_tup1(stream):
  204. return (_load(stream),)
  205. @register(_load_registry, TAG_TUP2)
  206. def _load_tup2(stream):
  207. return (_load(stream), _load(stream))
  208. @register(_load_registry, TAG_TUP3)
  209. def _load_tup3(stream):
  210. return (_load(stream), _load(stream), _load(stream))
  211. @register(_load_registry, TAG_TUP4)
  212. def _load_tup4(stream):
  213. return (_load(stream), _load(stream), _load(stream), _load(stream))
  214. @register(_load_registry, TAG_TUP_L1)
  215. def _load_tup_l1(stream):
  216. l, = I1.unpack(stream.read(1))
  217. return tuple(_load(stream) for i in range(l))
  218. @register(_load_registry, TAG_TUP_L4)
  219. def _load_tup_l4(stream):
  220. l, = I4.unpack(stream.read(4))
  221. return tuple(_load(stream) for i in xrange(l))
  222. @register(_load_registry, TAG_SLICE)
  223. def _load_slice(stream):
  224. start, stop, step = _load(stream)
  225. return slice(start, stop, step)
  226. @register(_load_registry, TAG_FSET)
  227. def _load_frozenset(stream):
  228. return frozenset(_load(stream))
  229. @register(_load_registry, TAG_INT_L1)
  230. def _load_int_l1(stream):
  231. l, = I1.unpack(stream.read(1))
  232. return int(stream.read(l))
  233. @register(_load_registry, TAG_INT_L4)
  234. def _load_int_l4(stream):
  235. l, = I4.unpack(stream.read(4))
  236. return int(stream.read(l))
  237. def _load(stream):
  238. tag = stream.read(1)
  239. if tag in IMM_INTS_LOADER:
  240. return IMM_INTS_LOADER[tag]
  241. return _load_registry.get(tag)(stream)
  242. #===============================================================================
  243. # API
  244. #===============================================================================
  245. def dump(obj):
  246. """dumps the given object to a byte-string representation"""
  247. stream = []
  248. _dump(obj, stream)
  249. return "".join(stream)
  250. def load(data):
  251. """loads the given byte-string representation to an object"""
  252. stream = StringIO(data)
  253. return _load(stream)
  254. simple_types = frozenset([type(None), int, long, bool, str, float, unicode,
  255. slice, complex, type(NotImplemented), type(Ellipsis)])
  256. def dumpable(obj):
  257. """indicates whether the object is dumpable by brine"""
  258. if type(obj) in simple_types:
  259. return True
  260. if type(obj) in (tuple, frozenset):
  261. return all(dumpable(item) for item in obj)
  262. return False
  263. if __name__ == "__main__":
  264. x = ("he", 7, u"llo", 8, (), 900, None, True, Ellipsis, 18.2, 18.2j + 13,
  265. slice(1,2,3), frozenset([5,6,7]), NotImplemented)
  266. assert dumpable(x)
  267. y = dump(x)
  268. z = load(y)
  269. assert x == z