diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3bf669a7f59f0df4f779d0b909150cf418e26c3..ca258b139b1b4588d3376a8e0fa47b6e0d1f31b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1 +1,4 @@
 # v2.18.3
+
+* fixed a bug with `get_remaining_bytes`
+* added `unpack_dict`
diff --git a/docs/coding/transforms.rst b/docs/coding/transforms.rst
index 702c54c160d7457863a7401468cdb2391a3bf0a3..4f44c5eef1958dbb3897dd6df09574e251f19553 100644
--- a/docs/coding/transforms.rst
+++ b/docs/coding/transforms.rst
@@ -1,6 +1,8 @@
 Rudimentary data transforms and algorithms
 ==========================================
 
+.. autofunction:: satella.coding.transforms.unpack_dict
+
 .. autofunction:: satella.coding.transforms.is_subset
 
 .. autoclass:: satella.coding.transforms.merge_list
diff --git a/satella/__init__.py b/satella/__init__.py
index 4c424a21848cdebf01103e4ecc8ddc7ddd1db505..340f88b41b31755248195bff8411706fcc057261 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.18.3a1'
+__version__ = '2.18.3'
diff --git a/satella/coding/transforms/__init__.py b/satella/coding/transforms/__init__.py
index 4104511eb284b4f60d58f37215e2ad4126e70f59..76c35cb3091de4e5380176399c5cabe87ad4cf77 100644
--- a/satella/coding/transforms/__init__.py
+++ b/satella/coding/transforms/__init__.py
@@ -16,9 +16,27 @@ from .predicates import is_subset
 __all__ = ['stringify', 'split_shuffle_and_join', 'one_tuple', 'none_if_false',
            'merge_series', 'pad_to_multiple_of_length', 'clip', 'hashables_to_int',
            'jsonify', 'intify', 'percentile', 'b64encode', 'linear_interpolate',
-           'merge_list', 'is_subset']
+           'merge_list', 'is_subset', 'unpack_dict']
 
-from satella.coding.typing import T, NoArgCallable, Appendable, Number, Predicate
+from satella.coding.typing import T, NoArgCallable, Appendable, Number, Predicate, K, V
+
+
+def unpack_dict(dct: tp.Dict[K, V], *args: K) -> tp.Iterator[V]:
+    """
+    Unpack a dictionary by accessing it's given keys in parallel.
+
+    Example:
+
+    >>> a, b, c = unpack_dict({1:2, 2:3, 4:5}, 1, 2, 4)
+    >>> assert a == 2 and b == 3 and c == 5
+
+    :param dct: dictionary to unpack
+    :param args: keys in this dictionary
+    :return: an iterator
+    :raises KeyError: a key was not found
+    """
+    for key in args:
+        yield dct[key]
 
 
 def none_if_false(y: tp.Any) -> tp.Optional[tp.Any]:
diff --git a/satella/parsing.py b/satella/parsing.py
index b9cac307394d716eaa5649f6f1e968ed0a6f49a7..5c93095b089675634949fcaa7b63c73d4f2a5859 100644
--- a/satella/parsing.py
+++ b/satella/parsing.py
@@ -12,13 +12,17 @@ class BinaryParser:
     This supports __len__ to return the amount of bytes in the stream,
     and __bytes__ to return the bytes.
 
-    :param b_stream: an object that allows indiced access, and allows subscripts to
-        span ranges, which will return items parseable by struct
+    This is a zero-copy solution, and :meth:`get_parser` will be zero copy
+    as well.
+
+    :param b_stream: an object that allows subscripts to
+        span ranges, which will return items parsable by struct
     :param offset: initial offset into the stream
     :param length: optional maximum length of byte count
     :raises NotEnoughBytes: offset larger than stream length
 
-    :ivar offset: offset from which bytes will be readed
+    :ivar pointer: pointer to the next bytes. Can be read and modified at will to
+        preserve the earlier state of the BinaryParser.
     """
     def __len__(self) -> int:
         return self.length
@@ -53,7 +57,9 @@ class BinaryParser:
 
     def assert_has_bytes(self, n: int) -> None:
         """
-        Assert that we have at least n bytes to consume
+        Assert that we have at least n bytes to consume.
+
+        This does not advance the pointer.
 
         :param n: amount of bytes to consume
         :raises NotEnoughBytes: not enough bytes remain in the stream!
@@ -65,7 +71,7 @@ class BinaryParser:
         """
         Return a subclassed binary parser providing a window to another binary parser's data.
 
-        This will advance the pointer by length
+        This will advance the pointer by length bytes
 
         :param length: amount of bytes to view
         :return: a BinaryParser
@@ -117,7 +123,8 @@ class BinaryParser:
 
         This must be a single-character struct!
 
-        This will advance the pointer by size of st.
+        This will advance the pointer by size of st. Struct objects
+        will be served from internal instance-specific cache.
 
         :param st: a single-character struct.Struct or a single character struct specification
         :return: a value returned from it
@@ -145,7 +152,8 @@ class BinaryParser:
         """
         Try to obtain as many bytes as this struct requires and return them parsed.
 
-        This will advance the pointer by size of st.
+        This will advance the pointer by size of st. Struct objects
+        will be served from internal instance-specific cache.
 
         :param st: a struct.Struct or a multi character struct specification
         :return: a tuple of un-parsed values
@@ -165,4 +173,6 @@ class BinaryParser:
 
         This will not advance the pointer
         """
-        return self.b_stream[self.pointer:self.pointer+self.length]
+        advance = self.pointer - self.init_ofs
+        remaining = self.length - advance
+        return self.b_stream[self.pointer:self.pointer+remaining]
diff --git a/tests/test_coding/test_transforms.py b/tests/test_coding/test_transforms.py
index 202767ab17b848dfe72cd08536c35bcb4c3d9829..93d7d2b170560612f485e38b134fadb4afb8eda2 100644
--- a/tests/test_coding/test_transforms.py
+++ b/tests/test_coding/test_transforms.py
@@ -5,11 +5,15 @@ import base64
 
 from satella.coding.transforms import stringify, split_shuffle_and_join, one_tuple, \
     merge_series, pad_to_multiple_of_length, clip, b64encode, linear_interpolate, \
-    hashables_to_int, none_if_false, merge_list, is_subset
+    hashables_to_int, none_if_false, merge_list, is_subset, unpack_dict
 
 
 class TestTransforms(unittest.TestCase):
 
+    def test_unpack_dict(self):
+        a, b, c = unpack_dict({1: 2, 2: 3, 4: 5}, 1, 2, 4)
+        self.assertTrue(a == 2 and b == 3 and c == 5)
+
     def test_is_subset(self):
         self.assertTrue(is_subset({}, {}))
         self.assertTrue(is_subset({}, {1: 2}))
diff --git a/tests/test_parsing.py b/tests/test_parsing.py
index 202cacf91c8f013a37d51e01c96aad64128d417e..b1c2e4ae5c5064010a65a2295f287119fe5f47ba 100644
--- a/tests/test_parsing.py
+++ b/tests/test_parsing.py
@@ -28,3 +28,5 @@ class TestParsing(unittest.TestCase):
         bp3 = bp.get_parser(4)
         self.assertEqual(bytes(bp3), b'\x00\x00\x00\xFF')
         self.assertRaises(NotEnoughBytes, lambda: bp2.get_parser(4))
+        bp3.skip(3)
+        self.assertEqual(bp3.get_remaining_bytes(), b'\xFF')