/// <summary> /// Check whether exactly the same bits are set into both bit arrays. /// </summary> /// <param name="other"></param> /// <returns>true if all bit values match, false otherwise.</returns> public bool HasSameContent(BitArray other) { if (_size != other._size) { return false; } for (int i = 0; i < _array.Length - 1; i++) { if (_array[i] != other._array[i]) return false; } // last uint32 might not be completed. int nbElts = _size % 32; UInt32 mask = 0xFFFFFFFF >> (32 - nbElts); // set to 0 all the bits which do not correspond to elements UInt32 last = mask & _array[_array.Length - 1]; UInt32 lastOther = mask & other._array[_array.Length - 1]; return (last == lastOther); }
/// <summary> /// Decode an ACK fragment payload /// </summary> /// <param name="payload">payload</param> /// <param name="lastFragmentSeqNumberReceived">last fragment sequence number received</param> /// <param name="fragmentMissingTable">table of indices of the missing fragments</param> public static void DecodeAckPayload(ref Frame payload, out byte lastFragmentSeqNumberReceived, out BitArray fragmentMissingTable) { if (payload.LengthDataUsed < 2) throw new ArgumentException("payload content too small", "payload"); lastFragmentSeqNumberReceived = payload.ReadByte(0); int nbFragments = (int)payload.ReadByte(1); byte[] byteArray = new byte[payload.LengthDataUsed - 2]; payload.ReadBytes(byteArray, 0, 2, payload.LengthDataUsed - 2); fragmentMissingTable = BitArray.FromByteArray(nbFragments, byteArray); Frame.Release(ref payload); }
/// <summary> /// Create a frame containing an ACK fragment /// </summary> /// <param name="messageSeqNumber">message sequence number</param> /// <param name="fragmentSeqNumber">fragment sequence number</param> /// <param name="pduSize">max size for the sublayer payload</param> /// <param name="reservedTail">max size for the sublayer tail</param> /// <param name="reservedHead">max size for the sublayer header</param> /// <param name="lastFragmentSeqNumberReceived">last fragment sequence number received by the receiver</param> /// <param name="fragmentMissingTable">table of missing fragments</param> /// <returns>the generated frame</returns> public static Frame CreateAckFragmentFrame(byte messageSeqNumber, byte fragmentSeqNumber, int pduSize, int reservedTail, int reservedHead, byte lastFragmentSeqNumberReceived, BitArray fragmentMissingTable) { byte[] byteArray = BitArray.ToByteArray(fragmentMissingTable); if (pduSize < 2 /* header */ + 1 /*last frag nb rec. */ + byteArray.Length + 1 /* size bit array */) throw new ArgumentException("pdu size is too small", "pduSize"); FragmentHeader header = new FragmentHeader(FragmentType.ACK, messageSeqNumber, false, fragmentSeqNumber); Frame frame = Frame.GetFrame(reservedHead + reservedTail + 2 + 1 + byteArray.Length + 1); frame.ReserveHeader(reservedHead); UInt16 serializedHdr = FragmentHeader.Encode(header); frame.AllocBack(4); frame.Write(0, serializedHdr); frame.Write(2, lastFragmentSeqNumberReceived); frame.Write(3, (byte)fragmentMissingTable.Length); frame.AppendToBack(byteArray, 0, byteArray.Length); return frame; }
internal override void HandleReceivedFragment(FragmentHeader header, ref Frame payload) { if (header.FragmentSeqNumber == _lastFragmentSeqNumberReceived) { #if DEBUG Trace.Print("Already received this fragment. Drop."); #endif Frame.Release(ref payload); return; } _lastFragmentSeqNumberReceived = header.FragmentSeqNumber; if ((header.Type != FragmentType.DATA) || (payload.LengthDataUsed < 2) || (_messageSeqNumber != header.MessageSeqNumber)) { Frame.Release(ref payload); return; } byte nbFragments; byte fragmentIndex; Frame data; try { Fragment.DecodeDataPayload(ref payload, out nbFragments, out fragmentIndex, out data); } catch (ArgumentException) { Frame.Release(ref payload); return; } #if PRINTALL Trace.Print("Receiving part " + fragmentIndex + "/" + nbFragments + " of packet " + _messageSeqNumber); #endif FragmentationMessageTerminatedEventArgs messageTerminatedHandlerArgs = null; bool sendAck = false; lock (_lock) { if (_currentState == FragmentationMessageState.Disposed) { Frame.Release(ref data); return; } if (_timerEnabled) { _timer.UnregisterItem(this); _timerEnabled = false; } if ((_fragmentMissingTable != null) && (nbFragments != _fragmentMissingTable.Length)) { // mismatch between messages: no the same number of fragments Frame.Release(ref data); return; } FragmentationMessageState oldState = _currentState; if (_sduCompleted) { Frame.Release(ref data); if (header.IsAckRequired) { // resend acknowlegment sendAck = true; } else { // leave the state unchanged return; } } else { switch (_currentState) { case FragmentationMessageState.Initial: case FragmentationMessageState.WaitingFragments: case FragmentationMessageState.WaitingSentStatus: case FragmentationMessageState.WaitingTimeoutBeforeResending: if (_currentState == FragmentationMessageState.Initial) { // first fragment received. _fragmentData = new Frame[nbFragments]; _fragmentMissingTable = new BitArray(nbFragments, true); } _fragmentMissingTable.Set(fragmentIndex, false); if (_fragmentData[fragmentIndex] == null) { _fragmentData[fragmentIndex] = data; } else { Frame.Release(ref data); } _sduCompleted = _fragmentMissingTable.CheckAllSet(false); if (header.IsAckRequired) { sendAck = true; } else { _currentState = FragmentationMessageState.WaitingFragments; } if (_sduCompleted) { _currentState = FragmentationMessageState.Final; messageTerminatedHandlerArgs = new FragmentationMessageTerminatedEventArgs(_currentState, Status.Success); } break; case FragmentationMessageState.WaitingAck: Frame.Release(ref data); throw new System.InvalidOperationException("Bad state."); case FragmentationMessageState.Disposed: case FragmentationMessageState.Final: case FragmentationMessageState.Unknown: default: Frame.Release(ref data); break; } } } if (sendAck) { SendAcknowledgement(); } if (messageTerminatedHandlerArgs != null) { OnFragmentationMessageTerminated(messageTerminatedHandlerArgs); } }
internal OutboundFragmentationMessage( UInt16 source, UInt16 destination, byte messageSeqNumber, DataRequestHandler lowerLayerRequestHandler, ref Frame sdu, Byte sduHandle, DataConfirmHandler handler, int maxPduSize, int reservedHeaderSize, int reservedTailSize, FragmentationMessageTimer timer ) : base(source, destination, messageSeqNumber, lowerLayerRequestHandler, maxPduSize, reservedHeaderSize, reservedTailSize, timer) { if (sdu.LengthDataUsed == 0) { Frame.Release(ref sdu); throw new ArgumentException("sdu is empty", "sdu"); } int payloadSize = maxPduSize - 2 - 2; // 2 for our own header, 1 for nb fragment, 1 for fragment id if (payloadSize == 0) { Frame.Release(ref sdu); throw new ArgumentException("PDU is too small", "maxPduSize"); } ushort flag = (ushort)(1 << 15); _isBroadcast = (destination & flag) > 0 ? true : false; _sdu = sdu; sdu = null; _sduHandle = sduHandle; _dataConfirmHandler = handler; _messageSeqNumber = messageSeqNumber; _timeoutForResending = c_defaultTimeoutForResending; _maxFragmentsBeforeAck = c_defaultMaxFragmentsBeforeAck; // compute required nb fragments and respective indices. int nbFragments = ((_sdu.LengthDataUsed % payloadSize) == 0) ? _sdu.LengthDataUsed / payloadSize : 1 + _sdu.LengthDataUsed / payloadSize; if (nbFragments > byte.MaxValue) throw new ArgumentException("sdu is too large."); _nbFragments = (byte)nbFragments; _endingIndices = new int[_nbFragments]; int lastIndex = -1; for (int i = 0; i + 1 < _nbFragments; i++) { lastIndex += payloadSize; _endingIndices[i] = lastIndex; } lastIndex += ((_sdu.LengthDataUsed % payloadSize) == 0) ? payloadSize : _sdu.LengthDataUsed % payloadSize; _endingIndices[_nbFragments - 1] = lastIndex; _successfullyTransmittedFragments = new BitArray(_nbFragments, false); _lastFragmentSent = new BitArray(_nbFragments, false); }
/// <summary> /// Clone the BitArray /// </summary> /// <returns>the clone BitArray</returns> public BitArray Clone() { BitArray clone = new BitArray(_size, false); Array.Copy(_array, clone._array, _array.Length); return clone; }
/// <summary> /// Converts a byte array (small indian: first byte includes first elements) into a BitArray /// </summary> /// <param name="nbElements">number of elements</param> /// <param name="array">encoding of the BitArray</param> /// <returns>decoded bitarray</returns> static public BitArray FromByteArray(int nbElements, byte[] array) { if (nbElements > array.Length * 8) throw new ArgumentException("nbElements"); BitArray bitArray = new BitArray(nbElements, false); int index = 0; int arrayLength = array.Length; for (int i = 0; index < arrayLength; i++) { for (int j = 0; j < 4 && index < array.Length; j++, index++) { bitArray._array[i] |= ((UInt32)array[index]) << (j * 8); } } return bitArray; }
/// <summary> /// Converts a BitArray into an array of bytes (small endian: first byte includes first elements) /// </summary> /// <param name="bitArray">the BitArray to be converted</param> /// <returns>the arry of bytes encoding the BitArray</returns> static public byte[] ToByteArray(BitArray bitArray) { int byteArrayLength = ((bitArray._size % 8) == 0) ? (bitArray._size / 8) : 1 + (bitArray._size / 8); byte[] byteArray = new byte[byteArrayLength]; int index = 0; for (int i = 0; index < byteArrayLength; i++) { UInt32 val = bitArray._array[i]; for (int j = 0; j < 4 && index < byteArrayLength; j++, index++) { byteArray[index] = (byte)(val & 0x000000FF); val = val >> 8; } } return byteArray; }
/// <summary> /// Performs the bitwise OR operation on the elements in the current BitArray against /// the corresponding elements in the specified BitArray /// </summary> /// <param name="value">The BitArray with which to perform the bitwise OR operation. </param> public void Or(BitArray value) { if (value.Length != this.Length) throw new ArgumentException("lenght mismatch.", "value"); for (int i = 0; i < _array.Length; i++) { this._array[i] = this._array[i] | value._array[i]; } }