commit b8b11a437b08cd949b112ebcd0e45bcf8f3a534e
parent e8a9b8fb5e86e1dc09356bae95bda71c54c31a52
Author: nolash <dev@holbrook.no>
Date: Mon, 12 Apr 2021 01:01:53 +0200
Adapt to ascend, descend states in librlp
Diffstat:
4 files changed, 155 insertions(+), 0 deletions(-)
diff --git a/rlpstream/decode.py b/rlpstream/decode.py
@@ -0,0 +1,60 @@
+# standard imports
+import logging
+import ctypes
+
+# local imports
+from . import (
+ LIBRLP_RLP_MAX_LIST_DEPTH,
+ librlp,
+ )
+
+logg = logging.getLogger().getChild(__name__)
+
+
+class RLPDecoder:
+
+ __nullptr = ctypes.POINTER(ctypes.c_void_p)()
+
+ def __init__(self, buffer_size):
+
+ class RLPDecoderBackend(ctypes.Structure):
+
+ _fields_ = [
+ ('buf', ctypes.POINTER(ctypes.c_char * buffer_size)),
+ ('alloc', ctypes.c_char),
+ ('depth', ctypes.c_int),
+ ('size', ctypes.c_int),
+ ('state', ctypes.c_int),
+ ('list_ptr', ctypes.POINTER(ctypes.POINTER(ctypes.c_char)) * LIBRLP_RLP_MAX_LIST_DEPTH),
+ ('ptr', ctypes.POINTER(ctypes.c_char)),
+ ]
+
+ self.buffer_size = buffer_size
+ self.backend = RLPEncoderBackend()
+ self.encoder = ctypes.pointer(self.backend)
+
+
+ def __del__(self):
+ logg.debug('free')
+ librlp.rlp_free(self.encoder)
+
+
+ def encode_item(self, v):
+ if isinstance(v, list):
+ librlp.rlp_descend(self.encoder)
+ for e in v:
+ self.encode_item(e)
+ librlp.rlp_ascend(self.encoder)
+
+ else:
+ b = (ctypes.c_char * len(v))(*v)
+ librlp.rlp_add(self.encoder, len(v), b)
+
+ return self.backend.size
+
+
+ def encode(self, v):
+ librlp.rlp_init(self.encoder, self.buffer_size, self.__nullptr)
+ r = self.encode_item(v)
+
+ return bytes(self.backend.buf.contents[:r])
diff --git a/rlpstream/encode.py b/rlpstream/encode.py
@@ -1,6 +1,8 @@
# standard imports
+import sys
import logging
import ctypes
+import enum
# local imports
from . import (
@@ -11,6 +13,15 @@ from . import (
logg = logging.getLogger().getChild(__name__)
+class RLPState(enum.IntEnum):
+ RLP_DECODE=0,
+ RLP_ENCODE=1,
+ RLP_LIST_ASCEND=2,
+ RLP_LIST_DESCEND=3,
+ RLP_STRING=4,
+ RLP_END=5,
+
+
class RLPEncoder:
__nullptr = ctypes.POINTER(ctypes.c_void_p)()
@@ -33,6 +44,10 @@ class RLPEncoder:
self.backend = RLPEncoderBackend()
self.encoder = ctypes.pointer(self.backend)
+ # decoder specific
+ self.zl = ctypes.pointer(ctypes.c_int())
+ self.buf = None
+
def __del__(self):
logg.debug('free')
@@ -58,3 +73,49 @@ class RLPEncoder:
r = self.encode_item(v)
return bytes(self.backend.buf.contents[:r])
+
+
+ def decode_item(self, frame, stack):
+ logg.debug('frame {} stack {} depth {}'.format(frame, stack, self.backend.depth))
+ r = librlp.rlp_next(self.encoder, self.zl, ctypes.pointer(self.buf))
+
+ if self.backend.state == RLPState.RLP_LIST_DESCEND:
+ stack.append(frame)
+ frame = []
+
+ elif self.backend.state == RLPState.RLP_LIST_ASCEND:
+ raise ValueError()
+ frame.append(stack.append(frame))
+
+ elif self.backend.state == RLPState.RLP_STRING:
+ l = int(self.zl.contents.value)
+ b = self.buf.contents.value[:l]
+ logg.debug('v {}'.format(b))
+ frame.append(b)
+
+ elif self.backend.state == RLPState.RLP_END:
+ stack = None
+
+ else:
+ raise AttributeError('unexpected state {}'.format(self.backend.state))
+
+ return (frame, stack)
+
+ def decode(self, v, size_hint=None):
+ if size_hint == None:
+ size_hint = sys.getsizeof(v)
+
+ in_buffer = ctypes.c_char * len(v)
+ in_buffer_p = in_buffer.from_buffer(bytearray(v))
+ librlp.rlp_init(self.encoder, len(v), in_buffer_p)
+
+ self.zl = ctypes.pointer(ctypes.c_int())
+ self.buf = ctypes.pointer((ctypes.c_char * size_hint)())
+
+ frame = []
+ stack = []
+ while stack != None:
+ (frame, stack) = self.decode_item(frame, stack)
+ l = self.backend.depth
+
+ return frame[0]
diff --git a/tests/test_rlp_decoder.py b/tests/test_rlp_decoder.py
@@ -0,0 +1,33 @@
+# standard imports
+import os
+import unittest
+import logging
+import ctypes
+
+# local imports
+from rlpstream.encode import RLPEncoder
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+
+class TestRlpDecoder(unittest.TestCase):
+
+ def setUp(self):
+ self.encoder = RLPEncoder(1024)
+
+
+ def test_decode_single_string(self):
+ b = b'\x83\x66\x6f\x6f'
+ v = self.encoder.decode(b)
+ self.assertEqual(v, b'foo')
+
+
+ def test_decode_single_string(self):
+ b = b'\xc4\x83\x66\x6f\x6f'
+ v = self.encoder.decode(b)
+ self.assertEqual(v, b'foo')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_rlp_encoder.py b/tests/test_rlp_encoder.py
@@ -17,6 +17,7 @@ class TestRlpEncoder(unittest.TestCase):
self.encoder = RLPEncoder(1024)
+ def test_encode_single_string(self):
v = b'foo'
b = self.encoder.encode(v)
print(b.hex())