class ElementReader(object): """ Create an ElementReader with the elementListener and an initial buffer for saving partial data. """ def __init__(self, elementListener): self._elementListener = elementListener self._tlvStructureDecoder = TlvStructureDecoder() self._usePartialData = False self._partialData = DynamicByteArray(1000) self._partialDataLength = 0 def onReceivedData(self, data): """ Continue to read data until the end of an element, then call elementListener.onReceivedElement(element). The buffer passed to onReceivedElement is only valid during this call. If you need the data later, you must copy. :param data: The buffer with the incoming element's bytes. :type data: An array type with int elements """ # Create a Blob and take its buf() since this creates a memoryview # which is more efficient for slicing. data = Blob(data, False).buf() # Process multiple objects in the data. while True: try: if not self._usePartialData: # This is the beginning of an element. if len(data) <= 0: # Wait for more data. return # Scan the input to check if a whole TLV element has been read. self._tlvStructureDecoder.seek(0) gotElementEnd = self._tlvStructureDecoder.findElementEnd(data) offset = self._tlvStructureDecoder.getOffset() except ValueError as ex: # Reset to read a new element on the next call. self._usePartialData = False self._tlvStructureDecoder = TlvStructureDecoder() raise ex if gotElementEnd: # Got the remainder of an element. Report to the caller. if self._usePartialData: # We have partial data from a previous call, so append this # data and use partialData for onReceivedElement. self._partialData.copy( data[:offset], self._partialDataLength) self._partialDataLength += offset # Create a Blob and take its buf() since this creates a # memoryview which is more efficient for slicing. partialDataView = Blob( self._partialData.getArray(), False).buf() element = partialDataView[:self._partialDataLength] # Assume we don't need to use partialData anymore until # needed. self._usePartialData = False else: # We are not using partialData, so just point to the input # data buffer. element = data[:offset] # Reset to read a new object. Do this before calling # onReceivedElement in case it throws an exception. data = data[offset:] self._tlvStructureDecoder = TlvStructureDecoder() self._elementListener.onReceivedElement(element) if len(data) == 0: # No more data in the packet. return # else loop back to decode. else: # Save remaining data for a later call. if not self._usePartialData: self._usePartialData = True self._partialDataLength = 0 if self._partialDataLength + len(data) > Common.MAX_NDN_PACKET_SIZE: # Reset to read a new element on the next call. self._usePartialData = False self._tlvStructureDecoder = TlvStructureDecoder() raise ValueError( "The incoming packet exceeds the maximum limit Face.getMaxNdnPacketSize()") self._partialData.copy(data, self._partialDataLength) self._partialDataLength += len(data) return
class ElementReader(object): """ Create an ElementReader with the elementListener and an initial buffer for saving partial data. """ def __init__(self, elementListener): self._elementListener = elementListener self._binaryXmlStructureDecoder = BinaryXmlStructureDecoder() self._tlvStructureDecoder = TlvStructureDecoder() self._usePartialData = False self._partialData = DynamicByteArray(1000) self._useTlv = None def onReceivedData(self, data): """ Continue to read data until the end of an element, then call elementListener.onReceivedElement(element). The buffer passed to onReceivedElement is only valid during this call. If you need the data later, you must copy. :param data: The buffer with the incoming element's bytes. :type data: An array type with int elements """ # Create a Blob and take its buf() since this creates a memoryview # which is more efficient for slicing. data = Blob(data, False).buf() # Process multiple objects in the data. while True: if not self._usePartialData: # This is the beginning of an element. Check whether it is # Binary XML or TLV. if len(data) <= 0: # Wait for more data. return # The type codes for TLV Interest and Data packets are chosen to not # conflict with the first byte of a binary XML packet, so we can # just look at the first byte. if (data[0] == Tlv.Interest or data[0] == Tlv.Data or data[0] == 0x80): self._useTlv = True else: # Binary XML. self._useTlv = False if self._useTlv: # Scan the input to check if a whole TLV element has been read. self._tlvStructureDecoder.seek(0) gotElementEnd = self._tlvStructureDecoder.findElementEnd(data) offset = self._tlvStructureDecoder.getOffset() else: # Scan the input to check if a whole Binary XML element has been # read. self._binaryXmlStructureDecoder.seek(0) gotElementEnd = self._binaryXmlStructureDecoder.findElementEnd(data) offset = self._binaryXmlStructureDecoder.getOffset() if gotElementEnd: # Got the remainder of an element. Report to the caller. if self._usePartialData: # We have partial data from a previous call, so append this # data and use partialData for onReceivedElement. self._partialData.copy( data[:offset], self._partialDataLength) self._partialDataLength += offset # Create a Blob and take its buf() since this creates a # memoryview which is more efficient for slicing. partialDataView = Blob( self._partialData.getArray(), False).buf() self._elementListener.onReceivedElement( partialDataView[:self._partialDataLength]) # Assume we don't need to use partialData anymore until # needed. self._usePartialData = False else: # We are not using partialData, so just point to the input # data buffer. self._elementListener.onReceivedElement(data[:offset]) # Need to read a new object. data = data[offset:] self._binaryXmlStructureDecoder = BinaryXmlStructureDecoder() self._tlvStructureDecoder = TlvStructureDecoder() if len(data) == 0: # No more data in the packet. return # else loop back to decode. else: # Save remaining data for a later call. if not self._usePartialData: self._usePartialData = True self._partialDataLength = 0 self._partialData.copy(data, self._partialDataLength) self._partialDataLength += len(data) return
class BinaryXmlStructureDecoder(object): """ Create and initialize a BinaryXmlStructureDecoder. """ def __init__(self): self._gotElementEnd = False self._offset = 0 self._level = 0 self._state = self.READ_HEADER_OR_CLOSE self._headerLength = 0 self._useHeaderBuffer = False # 10 bytes is enough to hold an encoded header with a type and a 64 bit value. self._headerBuffer = DynamicByteArray(10) self._nBytesToRead = 0 READ_HEADER_OR_CLOSE = 0 READ_BYTES = 1 def findElementEnd(self, input): """ Continue scanning input starting from self._offset to find the element end. If the end of the element which started at offset 0 is found, this returns True and getOffset() is the length of the element. Otherwise, this returns False which means you should read more into input and call again. :param input: The input buffer. You have to pass in input each time because the buffer could be reallocated. :type input: An array type with int elements :return: True if found the element end, False if not. :rtype: bool """ if self._gotElementEnd: # Someone is calling when we already got the end. return True decoder = BinaryXmlDecoder(input) while True: if self._offset >= len(input): # All the cases assume we have some input. return False if self._state == BinaryXmlStructureDecoder.READ_HEADER_OR_CLOSE: # First check for CLOSE. if (self._headerLength == 0 and input[self._offset] == BinaryXmlDecoder.CLOSE): self._offset += 1 # Close the level. self._level -= 1 if self._level == 0: # Finished. self._gotElementEnd = True return True if self._level < 0: raise RuntimeError( "BinaryXmlStructureDecoder: Unexpected close tag at offset " + repr(self._offset - 1)) # Get ready for the next header. self._startHeader() continue startingHeaderLength = self._headerLength while True: if self._offset >= len(input): # We can't get all of the header bytes from this input. # Save in headerBuffer. self._useHeaderBuffer = True nNewBytes = self._headerLength - startingHeaderLength self._headerBuffer.copy( input[self._offset - nNewBytes:self._offset], startingHeaderLength) return False headerByte = input[self._offset] self._offset += 1 self._headerLength += 1 if headerByte & BinaryXmlDecoder.TT_FINAL: # Break and read the header. break if self._useHeaderBuffer: # Copy the remaining bytes into headerBuffer. nNewBytes = self._headerLength - startingHeaderLength self._headerBuffer.copy( input[self._offset - nNewBytes:self._offset], startingHeaderLength) (type, value) = BinaryXmlDecoder( self._headerBuffer.getArray()).decodeTypeAndValue() else: # We didn't have to use the headerBuffer. decoder.seek(self._offset - self._headerLength) (type, value) = decoder.decodeTypeAndValue() # Set the next state based on the type. if type == BinaryXmlDecoder.DATTR: # We already consumed the item. READ_HEADER_OR_CLOSE again. # ndnb has rules about what must follow an attribute, but we # are just scanning. self._startHeader() elif (type == BinaryXmlDecoder.DTAG or type == BinaryXmlDecoder.EXT): # Start a new level and READ_HEADER_OR_CLOSE again. self._level += 1 self._startHeader() elif (type == BinaryXmlDecoder.TAG or type == BinaryXmlDecoder.ATTR): if type == BinaryXmlDecoder.TAG: # Start a new level and read the tag. self._level += 1 # Minimum tag or attribute length is 1. self._nBytesToRead = value + 1 self._state = BinaryXmlStructureDecoder.READ_BYTES # ndnb has rules about what must follow an attribute, but we # are just scanning. elif (type == BinaryXmlDecoder.BLOB or type == BinaryXmlDecoder.UDATA): self._nBytesToRead = value self._state = BinaryXmlStructureDecoder.READ_BYTES else: raise RuntimeError( "BinaryXmlStructureDecoder: Unrecognized header type " + repr(type)) elif self._state == BinaryXmlStructureDecoder.READ_BYTES: nRemainingBytes = len(input) - self._offset if nRemainingBytes < self._nBytesToRead: # Need more. self._offset += nRemainingBytes self._nBytesToRead -= nRemainingBytes return False # Got the bytes. Read a new header or close. self._offset += self._nBytesToRead self._startHeader() else: # We don't expect this to happen. raise RuntimeError( "BinaryXmlStructureDecoder: Unrecognized state " + repr(self._state)) def getOffset(self): """ Get the current offset into the input buffer. :return: The offset. :rtype: int """ return self._offset def seek(self, offset): """ Set the offset into the input, used for the next read. :param int offset: The new offset. """ self._offset = offset def _startHeader(self): """ A private method to set the state to READ_HEADER_OR_CLOSE and set up to start reading the header. """ self._headerLength = 0 self._useHeaderBuffer = False self._state = BinaryXmlStructureDecoder.READ_HEADER_OR_CLOSE
class ElementReader(object): """ Create an ElementReader with the elementListener and an initial buffer for saving partial data. """ def __init__(self, elementListener): self._elementListener = elementListener self._binaryXmlStructureDecoder = BinaryXmlStructureDecoder() self._tlvStructureDecoder = TlvStructureDecoder() self._usePartialData = False self._partialData = DynamicByteArray(1000) self._useTlv = None def onReceivedData(self, data): """ Continue to read data until the end of an element, then call elementListener.onReceivedElement(element). The buffer passed to onReceivedElement is only valid during this call. If you need the data later, you must copy. :param data: The buffer with the incoming element's bytes. :type data: An array type with int elements """ # Create a Blob and take its buf() since this creates a memoryview # which is more efficient for slicing. data = Blob(data, False).buf() # Process multiple objects in the data. while True: if not self._usePartialData: # This is the beginning of an element. Check whether it is # Binary XML or TLV. if len(data) <= 0: # Wait for more data. return # The type codes for TLV Interest and Data packets are chosen to not # conflict with the first byte of a binary XML packet, so we can # just look at the first byte. if (data[0] == Tlv.Interest or data[0] == Tlv.Data or data[0] == 0x80): self._useTlv = True else: # Binary XML. self._useTlv = False if self._useTlv: # Scan the input to check if a whole TLV element has been read. self._tlvStructureDecoder.seek(0) gotElementEnd = self._tlvStructureDecoder.findElementEnd(data) offset = self._tlvStructureDecoder.getOffset() else: # Scan the input to check if a whole Binary XML element has been # read. self._binaryXmlStructureDecoder.seek(0) gotElementEnd = self._binaryXmlStructureDecoder.findElementEnd( data) offset = self._binaryXmlStructureDecoder.getOffset() if gotElementEnd: # Got the remainder of an element. Report to the caller. if self._usePartialData: # We have partial data from a previous call, so append this # data and use partialData for onReceivedElement. self._partialData.copy(data[:offset], self._partialDataLength) self._partialDataLength += offset # Create a Blob and take its buf() since this creates a # memoryview which is more efficient for slicing. partialDataView = Blob(self._partialData.getArray(), False).buf() self._elementListener.onReceivedElement( partialDataView[:self._partialDataLength]) # Assume we don't need to use partialData anymore until # needed. self._usePartialData = False else: # We are not using partialData, so just point to the input # data buffer. self._elementListener.onReceivedElement(data[:offset]) # Need to read a new object. data = data[offset:] self._binaryXmlStructureDecoder = BinaryXmlStructureDecoder() self._tlvStructureDecoder = TlvStructureDecoder() if len(data) == 0: # No more data in the packet. return # else loop back to decode. else: # Save remaining data for a later call. if not self._usePartialData: self._usePartialData = True self._partialDataLength = 0 self._partialData.copy(data, self._partialDataLength) self._partialDataLength += len(data) return