aboutsummaryrefslogtreecommitdiff
path: root/frozen_deps/Cryptodome/Util
diff options
context:
space:
mode:
authorDeterminant <[email protected]>2024-08-23 03:14:03 +0000
committerDeterminant <[email protected]>2024-08-22 20:34:57 -0700
commit8d1c76ec7caf247d5675e14260d20fc508977ffb (patch)
tree8fa7c8ce3b7e3f4ece150a6da5922b5eb2dc7772 /frozen_deps/Cryptodome/Util
parent258780284151d49cba1d9c0d2ce33f9a19bb058b (diff)
release v0.1.8
Diffstat (limited to 'frozen_deps/Cryptodome/Util')
-rw-r--r--frozen_deps/Cryptodome/Util/Counter.py2
-rwxr-xr-xfrozen_deps/Cryptodome/Util/_cpuid_c.abi3.sobin12776 -> 19304 bytes
-rwxr-xr-xfrozen_deps/Cryptodome/Util/_cpuid_c.cpython-39-x86_64-linux-gnu.sobin10899 -> 0 bytes
-rw-r--r--frozen_deps/Cryptodome/Util/_raw_api.py6
-rwxr-xr-xfrozen_deps/Cryptodome/Util/_strxor.abi3.sobin14960 -> 20376 bytes
-rwxr-xr-xfrozen_deps/Cryptodome/Util/_strxor.cpython-39-x86_64-linux-gnu.sobin13213 -> 0 bytes
-rw-r--r--frozen_deps/Cryptodome/Util/asn1.py209
-rw-r--r--frozen_deps/Cryptodome/Util/asn1.pyi18
-rw-r--r--frozen_deps/Cryptodome/Util/number.py81
-rw-r--r--frozen_deps/Cryptodome/Util/py3compat.py11
10 files changed, 251 insertions, 76 deletions
diff --git a/frozen_deps/Cryptodome/Util/Counter.py b/frozen_deps/Cryptodome/Util/Counter.py
index c67bc95..269b5a7 100644
--- a/frozen_deps/Cryptodome/Util/Counter.py
+++ b/frozen_deps/Cryptodome/Util/Counter.py
@@ -51,6 +51,8 @@ def new(nbits, prefix=b"", suffix=b"", initial_value=1, little_endian=False, all
If ``False`` (default), in big endian format.
allow_wraparound (boolean):
This parameter is ignored.
+ An ``OverflowError`` exception is always raised when the counter wraps
+ around to zero.
Returns:
An object that can be passed with the :data:`counter` parameter to a CTR mode
cipher.
diff --git a/frozen_deps/Cryptodome/Util/_cpuid_c.abi3.so b/frozen_deps/Cryptodome/Util/_cpuid_c.abi3.so
index 60f1e26..51e31b7 100755
--- a/frozen_deps/Cryptodome/Util/_cpuid_c.abi3.so
+++ b/frozen_deps/Cryptodome/Util/_cpuid_c.abi3.so
Binary files differ
diff --git a/frozen_deps/Cryptodome/Util/_cpuid_c.cpython-39-x86_64-linux-gnu.so b/frozen_deps/Cryptodome/Util/_cpuid_c.cpython-39-x86_64-linux-gnu.so
deleted file mode 100755
index 718bec8..0000000
--- a/frozen_deps/Cryptodome/Util/_cpuid_c.cpython-39-x86_64-linux-gnu.so
+++ /dev/null
Binary files differ
diff --git a/frozen_deps/Cryptodome/Util/_raw_api.py b/frozen_deps/Cryptodome/Util/_raw_api.py
index c2e0187..cd64ac8 100644
--- a/frozen_deps/Cryptodome/Util/_raw_api.py
+++ b/frozen_deps/Cryptodome/Util/_raw_api.py
@@ -76,6 +76,12 @@ try:
if '__pypy__' not in sys.builtin_module_names and sys.flags.optimize == 2:
raise ImportError("CFFI with optimize=2 fails due to pycparser bug.")
+ # cffi still uses PyUnicode_GetSize, which was removed in Python 3.12
+ # thus leading to a crash on cffi.dlopen()
+ # See https://groups.google.com/u/1/g/python-cffi/c/oZkOIZ_zi5k
+ if sys.version_info >= (3, 12) and os.name == "nt":
+ raise ImportError("CFFI is not compatible with Python 3.12 on Windows")
+
from cffi import FFI
ffi = FFI()
diff --git a/frozen_deps/Cryptodome/Util/_strxor.abi3.so b/frozen_deps/Cryptodome/Util/_strxor.abi3.so
index c028978..f0f3784 100755
--- a/frozen_deps/Cryptodome/Util/_strxor.abi3.so
+++ b/frozen_deps/Cryptodome/Util/_strxor.abi3.so
Binary files differ
diff --git a/frozen_deps/Cryptodome/Util/_strxor.cpython-39-x86_64-linux-gnu.so b/frozen_deps/Cryptodome/Util/_strxor.cpython-39-x86_64-linux-gnu.so
deleted file mode 100755
index dd3fb45..0000000
--- a/frozen_deps/Cryptodome/Util/_strxor.cpython-39-x86_64-linux-gnu.so
+++ /dev/null
Binary files differ
diff --git a/frozen_deps/Cryptodome/Util/asn1.py b/frozen_deps/Cryptodome/Util/asn1.py
index a88f087..36f2d72 100644
--- a/frozen_deps/Cryptodome/Util/asn1.py
+++ b/frozen_deps/Cryptodome/Util/asn1.py
@@ -22,13 +22,20 @@
import struct
-from Cryptodome.Util.py3compat import byte_string, b, bchr, bord
+from Cryptodome.Util.py3compat import byte_string, bchr, bord
from Cryptodome.Util.number import long_to_bytes, bytes_to_long
-__all__ = ['DerObject', 'DerInteger', 'DerOctetString', 'DerNull',
- 'DerSequence', 'DerObjectId', 'DerBitString', 'DerSetOf']
+__all__ = ['DerObject', 'DerInteger', 'DerBoolean', 'DerOctetString',
+ 'DerNull', 'DerSequence', 'DerObjectId', 'DerBitString', 'DerSetOf']
+# Useful references:
+# - https://luca.ntop.org/Teaching/Appunti/asn1.html
+# - https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/
+# - https://www.zytrax.com/tech/survival/asn1.html
+# - https://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf
+# - https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
+# - https://misc.daniel-marschall.de/asn.1/oid-converter/online.php
def _is_number(x, only_non_negative=False):
test = 0
@@ -46,7 +53,7 @@ class BytesIO_EOF(object):
def __init__(self, initial_bytes):
self._buffer = initial_bytes
self._index = 0
- self._bookmark = None
+ self._bookmark = None
def set_bookmark(self):
self._bookmark = self._index
@@ -82,7 +89,7 @@ class DerObject(object):
"""Initialize the DER object according to a specific ASN.1 type.
:Parameters:
- asn1Id : integer
+ asn1Id : integer or byte
The universal DER tag number for this object
(e.g. 0x10 for a SEQUENCE).
If None, the tag is not known yet.
@@ -92,16 +99,20 @@ class DerObject(object):
the content octets).
If not specified, the payload is empty.
- implicit : integer
- The IMPLICIT tag number to use for the encoded object.
+ implicit : integer or byte
+ The IMPLICIT tag number (< 0x1F) to use for the encoded object.
It overrides the universal tag *asn1Id*.
+ It cannot be combined with the ``explicit`` parameter.
+ By default, there is no IMPLICIT tag.
constructed : bool
True when the ASN.1 type is *constructed*.
- False when it is *primitive*.
+ False when it is *primitive* (default).
- explicit : integer
- The EXPLICIT tag number to use for the encoded object.
+ explicit : integer or byte
+ The EXPLICIT tag number (< 0x1F) to use for the encoded object.
+ It cannot be combined with the ``implicit`` parameter.
+ By default, there is no EXPLICIT tag.
"""
if asn1Id is None:
@@ -125,23 +136,26 @@ class DerObject(object):
# context-spec | 1 0 (default for IMPLICIT/EXPLICIT)
# private | 1 1
#
+
+ constructed_bit = 0x20 if constructed else 0x00
+
if None not in (explicit, implicit):
raise ValueError("Explicit and implicit tags are"
" mutually exclusive")
if implicit is not None:
- self._tag_octet = 0x80 | 0x20 * constructed | self._convertTag(implicit)
- return
-
- if explicit is not None:
- self._tag_octet = 0xA0 | self._convertTag(explicit)
- self._inner_tag_octet = 0x20 * constructed | asn1Id
- return
-
- self._tag_octet = 0x20 * constructed | asn1Id
+ # IMPLICIT tag overrides asn1Id
+ self._tag_octet = 0x80 | constructed_bit | self._convertTag(implicit)
+ elif explicit is not None:
+ # 'constructed bit' is always asserted for an EXPLICIT tag
+ self._tag_octet = 0x80 | 0x20 | self._convertTag(explicit)
+ self._inner_tag_octet = constructed_bit | asn1Id
+ else:
+ # Neither IMPLICIT nor EXPLICIT
+ self._tag_octet = constructed_bit | asn1Id
def _convertTag(self, tag):
- """Check if *tag* is a real DER tag.
+ """Check if *tag* is a real DER tag (5 bits).
Convert it from a character to number if necessary.
"""
if not _is_number(tag):
@@ -306,7 +320,7 @@ class DerInteger(DerObject):
return DerObject.encode(self)
def decode(self, der_encoded, strict=False):
- """Decode a complete DER INTEGER DER, and re-initializes this
+ """Decode a DER-encoded INTEGER, and re-initializes this
object with it.
Args:
@@ -341,6 +355,89 @@ class DerInteger(DerObject):
self.value -= bits
+class DerBoolean(DerObject):
+ """Class to model a DER-encoded BOOLEAN.
+
+ An example of encoding is::
+
+ >>> from Cryptodome.Util.asn1 import DerBoolean
+ >>> bool_der = DerBoolean(True)
+ >>> print(bool_der.encode().hex())
+
+ which will show ``0101ff``, the DER encoding of True.
+
+ And for decoding::
+
+ >>> s = bytes.fromhex('0101ff')
+ >>> try:
+ >>> bool_der = DerBoolean()
+ >>> bool_der.decode(s)
+ >>> print(bool_der.value)
+ >>> except ValueError:
+ >>> print "Not a valid DER BOOLEAN"
+
+ the output will be ``True``.
+
+ :ivar value: The boolean value
+ :vartype value: boolean
+ """
+ def __init__(self, value=False, implicit=None, explicit=None):
+ """Initialize the DER object as a BOOLEAN.
+
+ Args:
+ value (boolean):
+ The value of the boolean. Default is False.
+
+ implicit (integer or byte):
+ The IMPLICIT tag number (< 0x1F) to use for the encoded object.
+ It overrides the universal tag for BOOLEAN (1).
+ It cannot be combined with the ``explicit`` parameter.
+ By default, there is no IMPLICIT tag.
+
+ explicit (integer or byte):
+ The EXPLICIT tag number (< 0x1F) to use for the encoded object.
+ It cannot be combined with the ``implicit`` parameter.
+ By default, there is no EXPLICIT tag.
+ """
+
+ DerObject.__init__(self, 0x01, b'', implicit, False, explicit)
+ self.value = value # The boolean value
+
+ def encode(self):
+ """Return the DER BOOLEAN, fully encoded as a binary string."""
+
+ self.payload = b'\xFF' if self.value else b'\x00'
+ return DerObject.encode(self)
+
+ def decode(self, der_encoded, strict=False):
+ """Decode a DER-encoded BOOLEAN, and re-initializes this object with it.
+
+ Args:
+ der_encoded (byte string): A DER-encoded BOOLEAN.
+
+ Raises:
+ ValueError: in case of parsing errors.
+ """
+
+ return DerObject.decode(self, der_encoded, strict)
+
+ def _decodeFromStream(self, s, strict):
+ """Decode a DER-encoded BOOLEAN from a file."""
+
+ # Fill up self.payload
+ DerObject._decodeFromStream(self, s, strict)
+
+ if len(self.payload) != 1:
+ raise ValueError("Invalid encoding for DER BOOLEAN: payload is not 1 byte")
+
+ if bord(self.payload[0]) == 0:
+ self.value = False
+ elif bord(self.payload[0]) == 0xFF:
+ self.value = True
+ else:
+ raise ValueError("Invalid payload for DER BOOLEAN")
+
+
class DerSequence(DerObject):
"""Class to model a DER SEQUENCE.
@@ -384,7 +481,7 @@ class DerSequence(DerObject):
"""
- def __init__(self, startSeq=None, implicit=None):
+ def __init__(self, startSeq=None, implicit=None, explicit=None):
"""Initialize the DER object as a SEQUENCE.
:Parameters:
@@ -392,12 +489,19 @@ class DerSequence(DerObject):
A sequence whose element are either integers or
other DER objects.
- implicit : integer
- The IMPLICIT tag to use for the encoded object.
+ implicit : integer or byte
+ The IMPLICIT tag number (< 0x1F) to use for the encoded object.
It overrides the universal tag for SEQUENCE (16).
+ It cannot be combined with the ``explicit`` parameter.
+ By default, there is no IMPLICIT tag.
+
+ explicit : integer or byte
+ The EXPLICIT tag number (< 0x1F) to use for the encoded object.
+ It cannot be combined with the ``implicit`` parameter.
+ By default, there is no EXPLICIT tag.
"""
- DerObject.__init__(self, 0x10, b'', implicit, True)
+ DerObject.__init__(self, 0x10, b'', implicit, True, explicit)
if startSeq is None:
self._seq = []
else:
@@ -434,6 +538,10 @@ class DerSequence(DerObject):
self._seq.append(item)
return self
+ def insert(self, index, item):
+ self._seq.insert(index, item)
+ return self
+
def hasInts(self, only_non_negative=True):
"""Return the number of items in this sequence that are
integers.
@@ -527,7 +635,6 @@ class DerSequence(DerObject):
self._seq.append(p.data_since_bookmark())
else:
derInt = DerInteger()
- #import pdb; pdb.set_trace()
data = p.data_since_bookmark()
derInt.decode(data, strict=strict)
self._seq.append(derInt.value)
@@ -648,19 +755,25 @@ class DerObjectId(DerObject):
binary string."""
comps = [int(x) for x in self.value.split(".")]
+
if len(comps) < 2:
raise ValueError("Not a valid Object Identifier string")
- self.payload = bchr(40*comps[0]+comps[1])
- for v in comps[2:]:
- if v == 0:
- enc = [0]
- else:
- enc = []
- while v:
- enc.insert(0, (v & 0x7F) | 0x80)
- v >>= 7
- enc[-1] &= 0x7F
- self.payload += b''.join([bchr(x) for x in enc])
+ if comps[0] > 2:
+ raise ValueError("First component must be 0, 1 or 2")
+ if comps[0] < 2 and comps[1] > 39:
+ raise ValueError("Second component must be 39 at most")
+
+ subcomps = [40 * comps[0] + comps[1]] + comps[2:]
+
+ encoding = []
+ for v in reversed(subcomps):
+ encoding.append(v & 0x7F)
+ v >>= 7
+ while v:
+ encoding.append((v & 0x7F) | 0x80)
+ v >>= 7
+
+ self.payload = b''.join([bchr(x) for x in reversed(encoding)])
return DerObject.encode(self)
def decode(self, der_encoded, strict=False):
@@ -687,15 +800,27 @@ class DerObjectId(DerObject):
# Derive self.value from self.payload
p = BytesIO_EOF(self.payload)
- comps = [str(x) for x in divmod(p.read_byte(), 40)]
+
+ subcomps = []
v = 0
while p.remaining_data():
c = p.read_byte()
- v = v*128 + (c & 0x7F)
+ v = (v << 7) + (c & 0x7F)
if not (c & 0x80):
- comps.append(str(v))
+ subcomps.append(v)
v = 0
- self.value = '.'.join(comps)
+
+ if len(subcomps) == 0:
+ raise ValueError("Empty payload")
+
+ if subcomps[0] < 40:
+ subcomps[:1] = [0, subcomps[0]]
+ elif subcomps[0] < 80:
+ subcomps[:1] = [1, subcomps[0] - 40]
+ else:
+ subcomps[:1] = [2, subcomps[0] - 80]
+
+ self.value = ".".join([str(x) for x in subcomps])
class DerBitString(DerObject):
@@ -736,7 +861,7 @@ class DerBitString(DerObject):
If not specified, the bit string is empty.
implicit : integer
The IMPLICIT tag to use for the encoded object.
- It overrides the universal tag for OCTET STRING (3).
+ It overrides the universal tag for BIT STRING (3).
explicit : integer
The EXPLICIT tag to use for the encoded object.
"""
diff --git a/frozen_deps/Cryptodome/Util/asn1.pyi b/frozen_deps/Cryptodome/Util/asn1.pyi
index dac023b..ee4891c 100644
--- a/frozen_deps/Cryptodome/Util/asn1.pyi
+++ b/frozen_deps/Cryptodome/Util/asn1.pyi
@@ -19,13 +19,19 @@ class DerObject:
def __init__(self, asn1Id: Optional[int]=None, payload: Optional[bytes]=..., implicit: Optional[int]=None,
constructed: Optional[bool]=False, explicit: Optional[int]=None) -> None: ...
def encode(self) -> bytes: ...
- def decode(self, der_encoded: bytes, strict: Optional[bool]=False) -> DerObject: ...
+ def decode(self, der_encoded: bytes, strict: bool=...) -> DerObject: ...
class DerInteger(DerObject):
value: int
def __init__(self, value: Optional[int]= 0, implicit: Optional[int]=None, explicit: Optional[int]=None) -> None: ...
def encode(self) -> bytes: ...
- def decode(self, der_encoded: bytes, strict: Optional[bool]=False) -> DerInteger: ...
+ def decode(self, der_encoded: bytes, strict: bool=...) -> DerInteger: ...
+
+class DerBoolean(DerObject):
+ value: bool
+ def __init__(self, value: bool=..., implicit: Optional[Union[int, bytes]]=..., explicit: Optional[Union[int, bytes]]=...) -> None: ...
+ def encode(self) -> bytes: ...
+ def decode(self, der_encoded: bytes, strict: bool=...) -> DerBoolean: ...
class DerSequence(DerObject):
def __init__(self, startSeq: Optional[Sequence[Union[int, DerInteger, DerObject]]]=None, implicit: Optional[int]=None) -> None: ...
@@ -41,7 +47,7 @@ class DerSequence(DerObject):
def hasInts(self, only_non_negative: Optional[bool]=True) -> int: ...
def hasOnlyInts(self, only_non_negative: Optional[bool]=True) -> bool: ...
def encode(self) -> bytes: ...
- def decode(self, der_encoded: bytes, strict: Optional[bool]=False, nr_elements: Optional[int]=None, only_ints_expected: Optional[bool]=False) -> DerSequence: ...
+ def decode(self, der_encoded: bytes, strict: bool=..., nr_elements: Optional[int]=None, only_ints_expected: Optional[bool]=False) -> DerSequence: ...
class DerOctetString(DerObject):
payload: bytes
@@ -54,13 +60,13 @@ class DerObjectId(DerObject):
value: str
def __init__(self, value: Optional[str]=..., implicit: Optional[int]=None, explicit: Optional[int]=None) -> None: ...
def encode(self) -> bytes: ...
- def decode(self, der_encoded: bytes, strict: Optional[bool]=False) -> DerObjectId: ...
+ def decode(self, der_encoded: bytes, strict: bool=...) -> DerObjectId: ...
class DerBitString(DerObject):
value: bytes
def __init__(self, value: Optional[bytes]=..., implicit: Optional[int]=None, explicit: Optional[int]=None) -> None: ...
def encode(self) -> bytes: ...
- def decode(self, der_encoded: bytes, strict: Optional[bool]=False) -> DerBitString: ...
+ def decode(self, der_encoded: bytes, strict: bool=...) -> DerBitString: ...
DerSetElement = Union[bytes, int]
@@ -70,5 +76,5 @@ class DerSetOf(DerObject):
def __iter__(self) -> Iterable: ...
def __len__(self) -> int: ...
def add(self, elem: DerSetElement) -> None: ...
- def decode(self, der_encoded: bytes, strict: Optional[bool]=False) -> DerObject: ...
+ def decode(self, der_encoded: bytes, strict: bool=...) -> DerObject: ...
def encode(self) -> bytes: ...
diff --git a/frozen_deps/Cryptodome/Util/number.py b/frozen_deps/Cryptodome/Util/number.py
index 5af85a3..6d59fd9 100644
--- a/frozen_deps/Cryptodome/Util/number.py
+++ b/frozen_deps/Cryptodome/Util/number.py
@@ -51,12 +51,8 @@ def size (N):
"""Returns the size of the number N in bits."""
if N < 0:
- raise ValueError("Size in bits only avialable for non-negative numbers")
-
- bits = 0
- while N >> bits:
- bits += 1
- return bits
+ raise ValueError("Size in bits only available for non-negative numbers")
+ return N.bit_length()
def getRandomInteger(N, randfunc=None):
@@ -113,27 +109,56 @@ def getRandomNBitInteger(N, randfunc=None):
assert size(value) >= N
return value
-def GCD(x,y):
- """Greatest Common Denominator of :data:`x` and :data:`y`.
- """
- x = abs(x) ; y = abs(y)
- while x > 0:
- x, y = y % x, x
- return y
+if sys.version_info[:2] >= (3, 5):
+
+ GCD = math.gcd
+
+else:
+
+ def GCD(x,y):
+ """Greatest Common Denominator of :data:`x` and :data:`y`.
+ """
+
+ x = abs(x) ; y = abs(y)
+ while x > 0:
+ x, y = y % x, x
+ return y
+
-def inverse(u, v):
- """The inverse of :data:`u` *mod* :data:`v`."""
+if sys.version_info[:2] >= (3, 8):
- u3, v3 = u, v
- u1, v1 = 1, 0
- while v3 > 0:
- q = u3 // v3
- u1, v1 = v1, u1 - v1*q
- u3, v3 = v3, u3 - v3*q
- while u1<0:
- u1 = u1 + v
- return u1
+ def inverse(u, v):
+ """The inverse of :data:`u` *mod* :data:`v`."""
+
+ if v == 0:
+ raise ZeroDivisionError("Modulus cannot be zero")
+ if v < 0:
+ raise ValueError("Modulus cannot be negative")
+
+ return pow(u, -1, v)
+
+else:
+
+ def inverse(u, v):
+ """The inverse of :data:`u` *mod* :data:`v`."""
+
+ if v == 0:
+ raise ZeroDivisionError("Modulus cannot be zero")
+ if v < 0:
+ raise ValueError("Modulus cannot be negative")
+
+ u3, v3 = u, v
+ u1, v1 = 1, 0
+ while v3 > 0:
+ q = u3 // v3
+ u1, v1 = v1, u1 - v1*q
+ u3, v3 = v3, u3 - v3*q
+ if u3 != 1:
+ raise ValueError("No inverse value can be computed")
+ while u1<0:
+ u1 = u1 + v
+ return u1
# Given a number of bits to generate and a random generation function,
# find a prime number of the appropriate size.
@@ -259,7 +284,7 @@ def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None):
# calculate range for X
# lower_bound = sqrt(2) * 2^{511 + 128*x}
# upper_bound = 2^{512 + 128*x} - 1
- x = (N - 512) >> 7;
+ x = (N - 512) >> 7
# We need to approximate the sqrt(2) in the lower_bound by an integer
# expression because floating point math overflows with these numbers
lower_bound = (14142135623730950489 * (2 ** (511 + 128*x))) // 10000000000000000000
@@ -366,12 +391,12 @@ def isPrime(N, false_positive_prob=1e-6, randfunc=None):
return N == 2
for p in sieve_base:
if N == p:
- return 1
+ return True
if N % p == 0:
- return 0
+ return False
rounds = int(math.ceil(-math.log(false_positive_prob)/math.log(4)))
- return _rabinMillerTest(N, rounds, randfunc)
+ return bool(_rabinMillerTest(N, rounds, randfunc))
# Improved conversion functions contributed by Barry Warsaw, after
diff --git a/frozen_deps/Cryptodome/Util/py3compat.py b/frozen_deps/Cryptodome/Util/py3compat.py
index 9a982e9..3294b66 100644
--- a/frozen_deps/Cryptodome/Util/py3compat.py
+++ b/frozen_deps/Cryptodome/Util/py3compat.py
@@ -87,6 +87,14 @@ if sys.version_info[0] == 2:
def byte_string(s):
return isinstance(s, str)
+ # In Python 2, a memoryview does not support concatenation
+ def concat_buffers(a, b):
+ if isinstance(a, memoryview):
+ a = a.tobytes()
+ if isinstance(b, memoryview):
+ b = b.tobytes()
+ return a + b
+
from StringIO import StringIO
BytesIO = StringIO
@@ -137,6 +145,9 @@ else:
def byte_string(s):
return isinstance(s, bytes)
+ def concat_buffers(a, b):
+ return a + b
+
from io import BytesIO
from io import StringIO
from sys import maxsize as maxint