pylibrlp

Python3 wrapper for librlp
git clone git://git.defalsify.org/pylibrlp.git
Log | Files | Refs | LICENSE

encoder.py (3441B)


      1 # standard imports
      2 import sys
      3 import logging
      4 import ctypes
      5 import enum
      6 
      7 # local imports
      8 from . import (
      9     LIBRLP_RLP_MAX_LIST_DEPTH,
     10     librlp,
     11         )
     12 
     13 logg = logging.getLogger().getChild(__name__)
     14 
     15 
     16 class RLPState(enum.IntEnum):
     17     RLP_DECODE=0,
     18     RLP_ENCODE=1,
     19     RLP_LIST_ASCEND=2,
     20     RLP_LIST_DESCEND=3,
     21     RLP_STRING=4,
     22     RLP_END=5,
     23 
     24 
     25 class RLPEncoder:
     26 
     27     __nullptr = ctypes.POINTER(ctypes.c_void_p)()
     28 
     29     def __init__(self, buffer_size):
     30 
     31         class RLPEncoderBackend(ctypes.Structure):
     32 
     33             _fields_ = [
     34                     ('buf', ctypes.POINTER(ctypes.c_char * buffer_size)),
     35                     ('alloc', ctypes.c_char),
     36                     ('depth', ctypes.c_int),
     37                     ('size', ctypes.c_int),
     38                     ('state', ctypes.c_int),
     39                     ('list_ptr', ctypes.POINTER(ctypes.POINTER(ctypes.c_char)) * LIBRLP_RLP_MAX_LIST_DEPTH),
     40                     ('ptr', ctypes.POINTER(ctypes.c_char)),
     41                     ]
     42     
     43         self.buffer_size = buffer_size
     44         self.backend = RLPEncoderBackend()
     45         self.encoder = ctypes.pointer(self.backend)
     46 
     47         # decoder specific
     48         self.zl = ctypes.pointer(ctypes.c_int())
     49         self.buf = None
     50 
     51 
     52     def __del__(self):
     53         logg.debug('free')
     54         librlp.rlp_free(self.encoder)
     55 
     56 
     57     def encode_item(self, v):
     58         if isinstance(v, list):
     59             librlp.rlp_descend(self.encoder)
     60             for e in v:
     61                 self.encode_item(e)
     62             librlp.rlp_ascend(self.encoder)
     63 
     64         else:
     65             b = (ctypes.c_char * len(v))(*v)
     66             librlp.rlp_add(self.encoder, len(v), b)
     67 
     68         return self.backend.size
     69 
     70 
     71     def encode(self, v):
     72         librlp.rlp_init(self.encoder, self.buffer_size, self.__nullptr)
     73         r = self.encode_item(v)
     74 
     75         return bytes(self.backend.buf.contents[:r])
     76 
     77 
     78     def decode_item(self, frame, stack):
     79         logg.debug('frame {} stack {} depth {}'.format(frame, stack, self.backend.depth))
     80         r = librlp.rlp_next(self.encoder, self.zl, ctypes.pointer(self.buf))
     81 
     82         if self.backend.state == RLPState.RLP_LIST_DESCEND:
     83             stack.append(frame)
     84             frame = []
     85 
     86         elif self.backend.state == RLPState.RLP_LIST_ASCEND:
     87             raise ValueError()
     88             frame.append(stack.append(frame))
     89 
     90         elif self.backend.state == RLPState.RLP_STRING:
     91             l = int(self.zl.contents.value)
     92             b = self.buf.contents[:l]
     93             logg.debug('v {}'.format(b))
     94             frame.append(b)
     95 
     96         elif self.backend.state == RLPState.RLP_END:
     97             stack = None
     98 
     99         else:
    100             raise AttributeError('unexpected state {}'.format(self.backend.state))
    101       
    102         return (frame, stack)
    103 
    104     def decode(self, v, size_hint=None):
    105 
    106         if size_hint == None:
    107             size_hint = sys.getsizeof(v)
    108 
    109         in_buffer = ctypes.c_char * len(v)
    110         in_buffer_p = in_buffer.from_buffer(bytearray(v))
    111         librlp.rlp_init(self.encoder, len(v), in_buffer_p)
    112 
    113         self.zl = ctypes.pointer(ctypes.c_int())
    114         self.buf = ctypes.pointer((ctypes.c_char * size_hint)())
    115 
    116         frame = []
    117         stack = []
    118         while stack != None:
    119             (frame, stack) = self.decode_item(frame, stack)
    120             l = self.backend.depth
    121 
    122         if isinstance(v, str):
    123             return frame[0]
    124         else:
    125             print('frame {}'.format(frame))
    126             return frame