Example #1
0
    def encode(self, encodable, with_constructor):
        """Encodes an encodable object.

        Args:
            encodable: an :class:`.Encodable` instance.
            with_constructor: ``True`` if the constructor should be
                prepended to the value.

        Returns
            bytes
        """
        constructor = b''
        value = self.get_encoder(encodable)(encodable.value)
        if with_constructor:
            sub, constructor = self.encode_constructor(encodable, value)

            # Prepend the length for variable length types, and the length
            # and count for collection types.
            if sub <= 0x9: # Empty or not variable, nothing to do
                pass
            elif sub == 0xA: # variable-one
                constructor += compat.to_bytes(len(value), 1, 'big')
            elif sub == 0xB: # variable-four
                constructor += compat.to_bytes(len(value), 4, 'big')
            else:
                raise NotImplementedError(hex(sub), value, encodable)
        return constructor + value
Example #2
0
def encode_constructor(default, short, zero, length, n=None, descriptor=None):
    """Encode a constructor based on the length of a value.

    Args:
        default: the default format code for the data type.
        short: an integer representing the short (1 octet) format
            code, if applicable.
        zero: an integer representing the zero-length format code,
            if applicable.
        length: an integer representing the binary length of the encoded
            value.
        n: an integer specifying the member count, for ``array``, ``map``
            and ``list`` types.
        descriptor: the AMQP-encoded representation of the type descriptor,
            may be None.
    """
    count = n or length
    format_code = default
    if length == 0 and zero:
        format_code = zero
    elif max(length + 1, count) < 256 and short:
        format_code = short

    subcategory = format_code >> 4
    format_code = compat.to_bytes(format_code, 1, 'big')
    return (
        subcategory,
        format_code if (descriptor is None)\
        else b'\x00' + descriptor + format_code
    )
Example #3
0
def encode_int(signed, length, small, zero, value):
    """Encode an integer to bytes.

    Args:
        signed: ``True`` if the integer should be represented signed.
        length: the default encoding length.
        small: ``True`` if the integer is allowed to be encoded as
            a single-octet value .e.g. `value` < 128/256.
        zero: ``True`` if the integer type support zero length encoding
            of ``0``.

    Returns:
        bytes
    """
    if zero and value == 0:
        return b''

    if ((signed and value < 128) or (not signed and value < 256))\
    and small:
        length = 1
    return compat.to_bytes(value, length, 'big', signed=signed)
Example #4
0
def encode_boolean(value):
    return compat.to_bytes(int(bool(value)), 1, 'big')
Example #5
0
 def _encode_length(self, sub, value):
     # Encode the length for variable-one and variable-four
     width = 1 if (sub == 0xA) else 4
     return compat.to_bytes(len(value), width, 'big') + value
Example #6
0
    def visit_collection(self, encodable):
        """Encode all members of the collection and calculate the length and
        count. For ``array`` instances, back-calculate the format codes for
        its members.
        """
        if encodable.is_list() and len(encodable) > 0:
            # For composite instances, NULL members after the mandatory fields
            # may be omitted. At this point, the composite is considered
            # validated so the trailing NULL members can be safely removed.

            while encodable[-1].is_empty():
                encodable.pop()
            pass

        # If the encodable is an array and it has no members, retur NULL. This
        # is a quick fix to prevent undecodable byte-sequences. Since the logic
        # below determines the member constructor based on the largest member,
        # empty arrays have no way of deciding what kind of format code they
        # should embed in their bode. So for the time being we return NULL instead,
        # since making the member type identifier mandatory on constructing Array
        # instances would break a lot of things.
        if encodable.is_array() and len(encodable) == 0:
            return b'\x40'

        members = [x.accept(self) for x in encodable]
        count = len(members)
        body = b''
        if encodable.is_array() and count > 0:
            # For array types, calculate the constructor based on the length
            # of the largest member.
            largest = max(members)
            ref_member = encodable[members.index(largest)]
            sub, ref_ctr = self.encode_constructor(
                ref_member, largest
            )

            # For variable types, encode the length for each member.
            if sub in (0xA, 0xB):
                body = b''.join([self._encode_length(sub, x) for x in members])
            else:
                body = b''.join(members)
            body = ref_ctr + body

        else:
            body = b''.join(members)

        # The size is the length of the body, plus one octed for the format
        # code. If the size exceeds 255, an unsigned integer (4 octets) is
        # used to indicate the size and count.
        size = len(body) + 1
        width = 1
        if size > 255:
            size += 3
            width = 4

        # Get the subcategory and the constructor for the collection. The size and
        # count are now known. The width of the length and count indicators is
        # determined by the size.
        body = compat.to_bytes(size, width, 'big')\
            + compat.to_bytes(count, width, 'big')\
            + body

        # Finally, encode the constructor. The format code is automatically
        # chosen based on the length of the body.
        sub, ctr = self.encode_constructor(encodable, body)

        return ctr + body