def decode()

in eden/scm/edenscm/mercurial/utils/cborutil.py [0:0]


    def decode(self, b, offset=0):
        """Attempt to decode bytes from an input buffer.

        ``b`` is a collection of bytes and ``offset`` is the byte
        offset within that buffer from which to begin reading data.

        ``b`` must support ``len()`` and accessing bytes slices via
        ``__slice__``. Typically ``bytes`` instances are used.

        Returns a tuple with the following fields:

        * Bool indicating whether values are available for retrieval.
        * Integer indicating the number of bytes that were fully consumed,
          starting from ``offset``.
        * Integer indicating the number of bytes that are desired for the
          next call in order to decode an item.
        """
        if not b:
            return bool(self._decodedvalues), 0, 0

        initialoffset = offset

        # We could easily split the body of this loop into a function. But
        # Python performance is sensitive to function calls and collections
        # are composed of many items. So leaving as a while loop could help
        # with performance. One thing that may not help is the use of
        # if..elif versus a lookup/dispatch table. There may be value
        # in switching that.
        while offset < len(b):
            # Attempt to decode an item. This could be a whole value or a
            # special value indicating an event, such as start or end of a
            # collection or indefinite length type.
            complete, value, readcount, special = decodeitem(b, offset)

            if readcount > 0:
                self.decodedbytecount += readcount

            if not complete:
                assert readcount < 0
                return (bool(self._decodedvalues), offset - initialoffset, -readcount)

            offset += readcount

            # No nested state. We either have a full value or beginning of a
            # complex value to deal with.
            if self._state == self._STATE_NONE:
                # A normal value.
                if special == SPECIAL_NONE:
                    self._decodedvalues.append(value)

                elif special == SPECIAL_START_ARRAY:
                    self._collectionstack.append({"remaining": value, "v": []})
                    self._state = self._STATE_WANT_ARRAY_VALUE

                elif special == SPECIAL_START_MAP:
                    self._collectionstack.append({"remaining": value, "v": {}})
                    self._state = self._STATE_WANT_MAP_KEY

                elif special == SPECIAL_START_SET:
                    self._collectionstack.append({"remaining": value, "v": set()})
                    self._state = self._STATE_WANT_SET_VALUE

                elif special == SPECIAL_START_INDEFINITE_BYTESTRING:
                    self._state = self._STATE_WANT_BYTESTRING_CHUNK_FIRST

                else:
                    raise CBORDecodeError("unhandled special state: %d" % special)

            # This value becomes an element of the current array.
            elif self._state == self._STATE_WANT_ARRAY_VALUE:
                # Simple values get appended.
                if special == SPECIAL_NONE:
                    c = self._collectionstack[-1]
                    c["v"].append(value)
                    c["remaining"] -= 1

                    # self._state doesn't need changed.

                # An array nested within an array.
                elif special == SPECIAL_START_ARRAY:
                    lastc = self._collectionstack[-1]
                    newvalue = []

                    lastc["v"].append(newvalue)
                    lastc["remaining"] -= 1

                    self._collectionstack.append({"remaining": value, "v": newvalue})

                    # self._state doesn't need changed.

                # A map nested within an array.
                elif special == SPECIAL_START_MAP:
                    lastc = self._collectionstack[-1]
                    newvalue = {}

                    lastc["v"].append(newvalue)
                    lastc["remaining"] -= 1

                    self._collectionstack.append({"remaining": value, "v": newvalue})

                    self._state = self._STATE_WANT_MAP_KEY

                elif special == SPECIAL_START_SET:
                    lastc = self._collectionstack[-1]
                    newvalue = set()

                    lastc["v"].append(newvalue)
                    lastc["remaining"] -= 1

                    self._collectionstack.append({"remaining": value, "v": newvalue})

                    self._state = self._STATE_WANT_SET_VALUE

                elif special == SPECIAL_START_INDEFINITE_BYTESTRING:
                    raise CBORDecodeError(
                        "indefinite length bytestrings " "not allowed as array values"
                    )

                else:
                    raise CBORDecodeError(
                        "unhandled special item when "
                        "expecting array value: %d" % special
                    )

            # This value becomes the key of the current map instance.
            elif self._state == self._STATE_WANT_MAP_KEY:
                if special == SPECIAL_NONE:
                    self._currentmapkey = value
                    self._state = self._STATE_WANT_MAP_VALUE

                elif special == SPECIAL_START_INDEFINITE_BYTESTRING:
                    raise CBORDecodeError(
                        "indefinite length bytestrings " "not allowed as map keys"
                    )

                elif special in (
                    SPECIAL_START_ARRAY,
                    SPECIAL_START_MAP,
                    SPECIAL_START_SET,
                ):
                    raise CBORDecodeError("collections not supported as map " "keys")

                # We do not allow special values to be used as map keys.
                else:
                    raise CBORDecodeError(
                        "unhandled special item when " "expecting map key: %d" % special
                    )

            # This value becomes the value of the current map key.
            elif self._state == self._STATE_WANT_MAP_VALUE:
                # Simple values simply get inserted into the map.
                if special == SPECIAL_NONE:
                    lastc = self._collectionstack[-1]
                    lastc["v"][self._currentmapkey] = value
                    lastc["remaining"] -= 1

                    self._state = self._STATE_WANT_MAP_KEY

                # A new array is used as the map value.
                elif special == SPECIAL_START_ARRAY:
                    lastc = self._collectionstack[-1]
                    newvalue = []

                    lastc["v"][self._currentmapkey] = newvalue
                    lastc["remaining"] -= 1

                    self._collectionstack.append({"remaining": value, "v": newvalue})

                    self._state = self._STATE_WANT_ARRAY_VALUE

                # A new map is used as the map value.
                elif special == SPECIAL_START_MAP:
                    lastc = self._collectionstack[-1]
                    newvalue = {}

                    lastc["v"][self._currentmapkey] = newvalue
                    lastc["remaining"] -= 1

                    self._collectionstack.append({"remaining": value, "v": newvalue})

                    self._state = self._STATE_WANT_MAP_KEY

                # A new set is used as the map value.
                elif special == SPECIAL_START_SET:
                    lastc = self._collectionstack[-1]
                    newvalue = set()

                    lastc["v"][self._currentmapkey] = newvalue
                    lastc["remaining"] -= 1

                    self._collectionstack.append({"remaining": value, "v": newvalue})

                    self._state = self._STATE_WANT_SET_VALUE

                elif special == SPECIAL_START_INDEFINITE_BYTESTRING:
                    raise CBORDecodeError(
                        "indefinite length bytestrings not " "allowed as map values"
                    )

                else:
                    raise CBORDecodeError(
                        "unhandled special item when "
                        "expecting map value: %d" % special
                    )

                self._currentmapkey = None

            # This value is added to the current set.
            elif self._state == self._STATE_WANT_SET_VALUE:
                if special == SPECIAL_NONE:
                    lastc = self._collectionstack[-1]
                    lastc["v"].add(value)
                    lastc["remaining"] -= 1

                elif special == SPECIAL_START_INDEFINITE_BYTESTRING:
                    raise CBORDecodeError(
                        "indefinite length bytestrings not " "allowed as set values"
                    )

                elif special in (
                    SPECIAL_START_ARRAY,
                    SPECIAL_START_MAP,
                    SPECIAL_START_SET,
                ):
                    raise CBORDecodeError("collections not allowed as set " "values")

                # We don't allow non-trivial types to exist as set values.
                else:
                    raise CBORDecodeError(
                        "unhandled special item when "
                        "expecting set value: %d" % special
                    )

            # This value represents the first chunk in an indefinite length
            # bytestring.
            elif self._state == self._STATE_WANT_BYTESTRING_CHUNK_FIRST:
                # We received a full chunk.
                if special == SPECIAL_NONE:
                    self._decodedvalues.append(bytestringchunk(value, first=True))

                    self._state = self._STATE_WANT_BYTESTRING_CHUNK_SUBSEQUENT

                # The end of stream marker. This means it is an empty
                # indefinite length bytestring.
                elif special == SPECIAL_INDEFINITE_BREAK:
                    # We /could/ convert this to a b''. But we want to preserve
                    # the nature of the underlying data so consumers expecting
                    # an indefinite length bytestring get one.
                    self._decodedvalues.append(
                        bytestringchunk(b"", first=True, last=True)
                    )

                    # Since indefinite length bytestrings can't be used in
                    # collections, we must be at the root level.
                    assert not self._collectionstack
                    self._state = self._STATE_NONE

                else:
                    raise CBORDecodeError(
                        "unexpected special value when "
                        "expecting bytestring chunk: %d" % special
                    )

            # This value represents the non-initial chunk in an indefinite
            # length bytestring.
            elif self._state == self._STATE_WANT_BYTESTRING_CHUNK_SUBSEQUENT:
                # We received a full chunk.
                if special == SPECIAL_NONE:
                    self._decodedvalues.append(bytestringchunk(value))

                # The end of stream marker.
                elif special == SPECIAL_INDEFINITE_BREAK:
                    self._decodedvalues.append(bytestringchunk(b"", last=True))

                    # Since indefinite length bytestrings can't be used in
                    # collections, we must be at the root level.
                    assert not self._collectionstack
                    self._state = self._STATE_NONE

                else:
                    raise CBORDecodeError(
                        "unexpected special value when "
                        "expecting bytestring chunk: %d" % special
                    )

            else:
                raise CBORDecodeError("unhandled decoder state: %d" % self._state)

            # We could have just added the final value in a collection. End
            # all complete collections at the top of the stack.
            while True:
                # Bail if we're not waiting on a new collection item.
                if self._state not in (
                    self._STATE_WANT_ARRAY_VALUE,
                    self._STATE_WANT_MAP_KEY,
                    self._STATE_WANT_SET_VALUE,
                ):
                    break

                # Or we are expecting more items for this collection.
                lastc = self._collectionstack[-1]

                if lastc["remaining"]:
                    break

                # The collection at the top of the stack is complete.

                # Discard it, as it isn't needed for future items.
                self._collectionstack.pop()

                # If this is a nested collection, we don't emit it, since it
                # will be emitted by its parent collection. But we do need to
                # update state to reflect what the new top-most collection
                # on the stack is.
                if self._collectionstack:
                    self._state = {
                        list: self._STATE_WANT_ARRAY_VALUE,
                        dict: self._STATE_WANT_MAP_KEY,
                        set: self._STATE_WANT_SET_VALUE,
                    }[type(self._collectionstack[-1]["v"])]

                # If this is the root collection, emit it.
                else:
                    self._decodedvalues.append(lastc["v"])
                    self._state = self._STATE_NONE

        return (bool(self._decodedvalues), offset - initialoffset, 0)