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)