Repository: megajanlott/cbor-decoder

Issue:
Map
Map states:
- [ ] Implement  MapInfo
- [ ] Implement  MapReadKey(n)
- [ ] Implement  MapReadValue(n)
- [ ] Implement  MapLen(n)
- [ ] Implement  MapInfKey()
- [ ] Implement  MapInfValue()

Additional info about states can be found here:
https://docs.google.com/document/d/1tvQJtJbYUcM2vI5H0RDukWqNYsLxjczZ30PiBFFVsV8/edit#

Comments:

Patch:
diff --git a/cbor/MajorType.py b/cbor/MajorType.py
index d9b0d34..2f43a8c 100644
--- a/cbor/MajorType.py
+++ b/cbor/MajorType.py
@@ -3,6 +3,7 @@ from cbor.State import State
 from cbor.type.ByteString import ByteString
 from cbor.type.TextString import TextString
 from cbor.type.Array import ArrayInfo
+from cbor.type.Map import MapInfo
 
 MAJOR_TYPE_MASK = 0b11100000
 MAJOR_TYPE_SIZE = 3
@@ -26,5 +27,7 @@ class MajorType(State):
             return TextString()
         elif t == 4:
             return ArrayInfo()
+        elif t == 5:
+            return MapInfo()
 
         return
diff --git a/cbor/type/Map.py b/cbor/type/Map.py
new file mode 100644
index 0000000..e46f64e
--- /dev/null
+++ b/cbor/type/Map.py
@@ -0,0 +1,84 @@
+import cbor.CBORStream
+import cbor.MajorType
+import cbor.State
+
+
+class MapInfo(cbor.State.State):
+
+    def run(self, stream: cbor.CBORStream.CBORStream, handler):
+        info = stream.read(1)
+        length = ord(info) & 0b00011111
+        handler('{')
+        if length == 0:
+            handler('}')
+        elif length < 24:
+            return [MapReadValue(length), cbor.MajorType.MajorType()]
+        elif length == 24:
+            return [MapLen(1)]
+        elif length == 25:
+            return [MapLen(2)]
+        elif length == 26:
+            return [MapLen(4)]
+        elif length == 27:
+            return [MapLen(8)]
+        elif length == 31:
+            return [MapInfValue(), cbor.MajorType.MajorType()]
+        return []
+
+
+class MapLen(cbor.State.State):
+
+    def __eq__(self, other):
+        return self.n == other.n
+
+    def __init__(self, n: int):
+        self.n = n
+
+    def run(self, stream: cbor.CBORStream.CBORStream, handler):
+        info = stream.read(self.n)
+        length = int.from_bytes(info, byteorder='big')
+        return [MapReadValue(length), cbor.MajorType.MajorType()]
+
+
+class MapReadKey(cbor.State.State):
+
+    def __eq__(self, other):
+        return self.n == other.n
+
+    def __init__(self, n: int):
+        self.n = n
+
+    def run(self, stream: cbor.CBORStream.CBORStream, handler):
+        if self.n == 0:
+            handler('}')
+            return []
+        if self.n > 0:
+            handler(',')
+        return [MapReadValue(self.n), cbor.MajorType.MajorType()]
+
+
+class MapReadValue(cbor.State.State):
+
+    def __eq__(self, other):
+        return self.n == other.n
+
+    def __init__(self, n: int):
+        self.n = n
+
+    def run(self, stream: cbor.CBORStream.CBORStream, handler):
+        handler(':')
+        return [MapReadKey(self.n-1), cbor.MajorType.MajorType()]
+
+
+class MapInfKey(cbor.State.State):
+
+    def run(self, stream: cbor.CBORStream.CBORStream, handler):
+        handler(',')
+        return [MapInfValue(), cbor.MajorType.MajorType()]
+
+
+class MapInfValue(cbor.State.State):
+
+    def run(self, stream: cbor.CBORStream.CBORStream, handler):
+        handler(':')
+        return [MapInfKey(), cbor.MajorType.MajorType()]


Test patch:
diff --git a/tests/test_Map.py b/tests/test_Map.py
new file mode 100644
index 0000000..7497c8d
--- /dev/null
+++ b/tests/test_Map.py
@@ -0,0 +1,147 @@
+from io import BytesIO
+from cbor.MajorType import MajorType
+from cbor.CBORStream import CBORStream
+from cbor.type.Map import *
+from tests.MockHandler import MockHandler
+from cbor.Decoder import Decoder
+
+
+def ignore_handler(v):
+    return
+
+
+def test_run_map_probe():
+    data = CBORStream(BytesIO(bytes([0b10100001])))
+    assert type(MajorType().run(data, None)) == MapInfo
+
+
+def test_run_map_length():
+    # Map length lower than 24.
+    data = CBORStream(BytesIO(bytes([0b10100011])))
+    stack = MapInfo().run(data, ignore_handler)
+    assert len(stack) == 2
+    assert type(stack[0]) == MapReadValue
+    assert stack[0] == MapReadValue(3)
+    assert type(stack[1]) == MajorType
+
+
+def test_run_map_length_multibyte():
+    # Map length on 1 byte.
+    data = CBORStream(BytesIO(bytes([
+        0b10111000, 0b1
+    ])))
+    stack = MapInfo().run(data, ignore_handler)
+    assert len(stack) == 1
+    assert type(stack[0]) == MapLen
+    assert stack[0] == MapLen(1)
+    stack2 = stack[0].run(data, ignore_handler)
+    assert len(stack2) == 2
+    assert type(stack2[0]) == MapReadValue
+    assert stack2[0] == MapReadValue(1)
+    assert type(stack2[1]) == MajorType
+
+    # Map length on 2 bytes.
+    data = CBORStream(BytesIO(bytes([
+        0b10111001, 0b1, 0b0
+    ])))
+    stack = MapInfo().run(data, ignore_handler)
+    assert len(stack) == 1
+    assert type(stack[0]) == MapLen
+    assert stack[0] == MapLen(2)
+    stack2 = stack[0].run(data, ignore_handler)
+    assert len(stack2) == 2
+    assert type(stack2[0]) == MapReadValue
+    assert stack2[0] == MapReadValue(1 << 8)
+    assert type(stack2[1]) == MajorType
+
+    # Map length on 4 bytes.
+    data = CBORStream(BytesIO(bytes([
+        0b10111010, 0b1, 0b0, 0b0, 0b0
+    ])))
+    stack = MapInfo().run(data, ignore_handler)
+    assert len(stack) == 1
+    assert type(stack[0]) == MapLen
+    assert stack[0] == MapLen(4)
+    stack2 = stack[0].run(data, ignore_handler)
+    assert len(stack2) == 2
+    assert type(stack2[0]) == MapReadValue
+    assert stack2[0] == MapReadValue(1 << 24)
+    assert type(stack2[1]) == MajorType
+
+    # Map length on 8 bytes.
+    data = CBORStream(BytesIO(bytes([
+        0b10111011, 0b1, 0b0, 0b0, 0b0, 0b0, 0b0, 0b0, 0b0
+    ])))
+    stack = MapInfo().run(data, ignore_handler)
+    assert len(stack) == 1
+    assert type(stack[0]) == MapLen
+    assert stack[0] == MapLen(8)
+    stack2 = stack[0].run(data, ignore_handler)
+    assert len(stack2) == 2
+    assert type(stack2[0]) == MapReadValue
+    assert stack2[0] == MapReadValue(1 << 56)
+    assert type(stack2[1]) == MajorType
+
+
+def test_run_map_inf():
+    # Map with infinite length.
+    data = CBORStream(BytesIO(bytes([0b10111111])))
+    stack = MapInfo().run(data, ignore_handler)
+    assert len(stack) == 2
+    assert type(stack[0]) == MapInfValue
+    assert type(stack[1]) == MajorType
+
+
+def test_run_map_inf_key():
+    # Map with infinite length.
+    data = CBORStream(BytesIO(bytes([])))
+    stack = MapInfKey().run(data, ignore_handler)
+    assert len(stack) == 2
+    assert type(stack[0]) == MapInfValue
+    assert type(stack[1]) == MajorType
+
+
+def test_run_map_inf_value():
+    # Map with infinite length.
+    data = CBORStream(BytesIO(bytes([])))
+    stack = MapInfValue().run(data, ignore_handler)
+    assert len(stack) == 2
+    assert type(stack[0]) == MapInfKey
+    assert type(stack[1]) == MajorType
+
+
+def test_run_map_info_empty():
+    handler = MockHandler()
+
+    # Empty array.
+    data = CBORStream(BytesIO(bytes([0b10100000])))
+    stack = MapInfo().run(data, handler.handler)
+    assert len(stack) == 0
+    handler.assert_data('{}')
+
+
+def test_run_map_single_element():
+    handler = MockHandler()
+
+    # Empty array.
+    d = Decoder()
+    data = bytes([0b10100001, 0b10100000, 0b10100000])
+    d.decode_array(data, handler.handler)
+    print(handler.data)
+    handler.assert_data('{{}:{}}')
+
+
+def test_run_map_two_elements():
+    handler = MockHandler()
+
+    # Empty array.
+    d = Decoder()
+    data = bytes([
+        0b10100010,
+        0b10100000,
+        0b10100000,
+        0b10100000,
+        0b10100000])
+    d.decode_array(data, handler.handler)
+    print(handler.data)
+    handler.assert_data('{{}:{},{}:{}}')