예제 #1
0
        private int ProcessPrelude(byte[] data, int offset, int length)
        {
            /* this is absolutely redundant, but since the totalLength field will result
             *     in a potentially huge allocation, we want to fail fast before even attempting to continue
             *     if the totalLength field has been corrupted. */
            _runningChecksumStream.Write(_workingBuffer, 0, EventStreamMessage.PreludeLen - EventStreamMessage.SizeOfInt32);
            var preludeChecksum = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt32(_workingBuffer,
                                                                                                  EventStreamMessage.PreludeLen - EventStreamMessage.SizeOfInt32));

            if (preludeChecksum != _runningChecksumStream.Crc32)
            {
                _state = DecoderState.Error;
                throw new EventStreamChecksumFailureException(
                          string.Format(CultureInfo.InvariantCulture, "Message Prelude Checksum failure. Expected {0} but was {1}", preludeChecksum, _runningChecksumStream.Crc32));
            }

            _runningChecksumStream.Write(_workingBuffer, EventStreamMessage.PreludeLen - 4, EventStreamMessage.SizeOfInt32);
            _currentMessageLength = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt32(_workingBuffer, 0));

            /* It's entirely possible to change this to not do this potentially large allocation
             * but it complicates the API a bit and is most likely unnecessary. For now, just allocate
             * the entire message buffer. It will be released after each message is processed. */
            _workingMessage = new byte[_currentMessageLength];
            Buffer.BlockCopy(_workingBuffer, 0, _workingMessage, 0, EventStreamMessage.PreludeLen);
            _state = DecoderState.ReadMessage;
            return(0);
        }
예제 #2
0
        /// <summary>
        /// Writes this Header to buffer starting at offset
        /// Keep in mind, this API assumes buffer is large enough
        /// for the operation.
        /// </summary>
        /// <param name="buffer">buffer to serialize this Header to</param>
        /// <param name="offset">offset to begin writing at.</param>
        /// <returns>
        /// the new offset.
        /// </returns>
        public int WriteToBuffer(byte[] buffer, int offset)
        {
            var newOffset = offset;

            buffer[newOffset++] = (byte)Name.Length;
            var nameBytes = Encoding.UTF8.GetBytes(Name);

            Buffer.BlockCopy(nameBytes, 0, buffer, newOffset, Name.Length);
            newOffset += Name.Length;

            buffer[newOffset++] = (byte)HeaderType;
            byte[] serializedBytes = null;
            int    valueLength     = 0;

            switch (HeaderType)
            {
            case EventStreamHeaderType.BoolTrue:
            case EventStreamHeaderType.BoolFalse:
                break;

            case EventStreamHeaderType.Byte:
                buffer[newOffset++] = (byte)HeaderValue;
                break;

            case EventStreamHeaderType.Int16:
                serializedBytes = BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder((Int16)HeaderValue));
                Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 2);
                newOffset += _sizeOfInt16;
                break;

            case EventStreamHeaderType.Int32:
                serializedBytes = BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder((Int32)HeaderValue));
                Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 4);
                newOffset += _sizeOfInt32;
                break;

            case EventStreamHeaderType.Int64:
                serializedBytes = BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder((Int64)HeaderValue));
                Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 8);
                newOffset += _sizeOfInt64;
                break;

            case EventStreamHeaderType.ByteBuf:
                serializedBytes = HeaderValue as byte[];
                valueLength     = serializedBytes.Length;
                Buffer.BlockCopy(BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder((Int16)valueLength)), 0,
                                 buffer, newOffset, 2);
                newOffset += _sizeOfInt16;
                Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, valueLength);
                newOffset += valueLength;
                break;

            case EventStreamHeaderType.String:
                serializedBytes = Encoding.UTF8.GetBytes(HeaderValue as string);
                valueLength     = serializedBytes.Length;
                Buffer.BlockCopy(BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder((Int16)valueLength)), 0,
                                 buffer, newOffset, 2);
                newOffset += _sizeOfInt16;
                Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, valueLength);
                newOffset += valueLength;
                break;

            case EventStreamHeaderType.Timestamp:
                var tempValue = (Int64)((DateTime)HeaderValue).Subtract(_unixEpoch).TotalMilliseconds;
                serializedBytes = BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder(tempValue));
                Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, 8);
                newOffset += _sizeOfInt64;
                break;

            case EventStreamHeaderType.UUID:
                serializedBytes = ((Guid)HeaderValue).ToByteArray();
                Buffer.BlockCopy(serializedBytes, 0, buffer, newOffset, serializedBytes.Length);
                newOffset += serializedBytes.Length;
                break;

            default:
                throw new EventStreamParseException(string.Format(CultureInfo.InvariantCulture, "Header Type: {0} is an unknown type.", HeaderType));
            }

            return(newOffset);
        }
예제 #3
0
        public static EventStreamHeader FromBuffer(byte[] buffer, int offset, ref int newOffset)
        {
            newOffset = offset;
            byte nameLength = buffer[newOffset++];
            var  header     = new EventStreamHeader(Encoding.UTF8.GetString(buffer, newOffset, nameLength));

            newOffset += nameLength;

            header.HeaderType = (EventStreamHeaderType)buffer[newOffset++];

            Int16 valueLength = 0;

            switch (header.HeaderType)
            {
            case EventStreamHeaderType.BoolTrue:
                header.HeaderValue = true;
                break;

            case EventStreamHeaderType.BoolFalse:
                header.HeaderValue = false;
                break;

            case EventStreamHeaderType.Byte:
                header.HeaderValue = buffer[newOffset];
                newOffset         += _sizeOfByte;
                break;

            case EventStreamHeaderType.Int16:
                header.HeaderValue = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt16(buffer, newOffset));
                newOffset         += _sizeOfInt16;
                break;

            case EventStreamHeaderType.Int32:
                header.HeaderValue = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt32(buffer, newOffset));
                newOffset         += _sizeOfInt32;
                break;

            case EventStreamHeaderType.Int64:
                header.HeaderValue = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt64(buffer, newOffset));
                newOffset         += _sizeOfInt64;
                break;

            case EventStreamHeaderType.ByteBuf:
                valueLength        = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt16(buffer, newOffset));
                newOffset         += _sizeOfInt16;
                header.HeaderValue = new byte[valueLength];
                Buffer.BlockCopy(buffer, newOffset, header.HeaderValue as byte[], 0, valueLength);
                newOffset += valueLength;
                break;

            case EventStreamHeaderType.String:
                valueLength        = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt16(buffer, newOffset));
                newOffset         += _sizeOfInt16;
                header.HeaderValue = Encoding.UTF8.GetString(buffer, newOffset, valueLength);
                newOffset         += valueLength;
                break;

            case EventStreamHeaderType.Timestamp:
                Int64 tempValue = EndianConversionUtility.NetworkToHostOrder(BitConverter.ToInt64(buffer, newOffset));
                newOffset += _sizeOfInt64;
                //keep in mind on the windows APIs (and hence NetStandard as well) the epoch is 1/1/1900,
                //and we're using unix epoch. So we compensate here.
                header.HeaderValue = _unixEpoch.AddMilliseconds(tempValue);
                break;

            case EventStreamHeaderType.UUID:
                var guidCpy = new byte[16];
                valueLength = _sizeOfGuid;
                Buffer.BlockCopy(buffer, newOffset, guidCpy, 0, valueLength);
                newOffset         += valueLength;
                header.HeaderValue = new Guid(guidCpy);
                break;

            default:
                throw new EventStreamParseException(string.Format(CultureInfo.InvariantCulture, "Header Type: {0} is an unknown type.", header.HeaderType));
            }

            return(header);
        }
예제 #4
0
        /// <summary>
        /// Converts a message into a byte buffer (usually for network transmission).
        /// </summary>
        public byte[] ToByteArray()
        {
            int headersWireLength = 0;

            //first we need to figure out how much space the headers will take up.
            if (Headers != null)
            {
                foreach (var header in Headers)
                {
                    headersWireLength += header.Value.GetWireSize();
                }
            }

            var payloadLength = Payload?.Length ?? 0;
            //total message length is the framing size + the payload size + the headers wire size.
            var totalLength = headersWireLength + payloadLength + FramingSize;

            var messageBuffer = new byte[totalLength];

            //now write the total length and the headers length to the message. make sure to handle endianness conversions
            var offset = 0;

            Buffer.BlockCopy(BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder(totalLength)), 0,
                             messageBuffer, offset, SizeOfInt32);
            offset += SizeOfInt32;
            Buffer.BlockCopy(BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder(headersWireLength)), 0,
                             messageBuffer, offset, SizeOfInt32);
            offset += SizeOfInt32;

            using (var nullStream = new NullStream())
                using (var runningChecksum = new CrcCalculatorStream(nullStream))
                {
                    //write the total length and headers length to the checksum stream.
                    runningChecksum.Write(messageBuffer, 0, offset);
                    //take the current checksum and write it to the message.
                    Buffer.BlockCopy(BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder(runningChecksum.Crc32)), 0,
                                     messageBuffer, offset, SizeOfInt32);
                    //now take the current checksum and write it to the checksum stream.
                    runningChecksum.Write(messageBuffer, offset, SizeOfInt32);
                    offset += SizeOfInt32;

                    //loop over the headers and write them out to the message.
                    if (Headers != null)
                    {
                        foreach (var header in Headers)
                        {
                            offset = header.Value.WriteToBuffer(messageBuffer, offset);
                        }
                        //make sure to add the header bytes to the checksum stream.
                        runningChecksum.Write(messageBuffer, PreludeLen, offset - PreludeLen);
                    }

                    //write the payload to the message.
                    if (Payload != null)
                    {
                        Buffer.BlockCopy(Payload, 0, messageBuffer, offset, Payload.Length);
                        //update the checksum
                        runningChecksum.Write(messageBuffer, offset, Payload.Length);
                        offset += Payload.Length;
                    }

                    //take the final checksum and add it to the end of the message.
                    Buffer.BlockCopy(BitConverter.GetBytes(EndianConversionUtility.HostToNetworkOrder(runningChecksum.Crc32)), 0,
                                     messageBuffer, offset, SizeOfInt32);
                }

            return(messageBuffer);
        }
예제 #5
0
        /// <summary>
        /// Builds a message from buffer.
        /// </summary>
        /// <param name="buffer">buffer to read</param>
        /// <param name="offset">offset to start reading</param>
        /// <param name="length">buffer length.</param>
        /// <returns>
        /// parsed instance of EventStreamMessage. Doesn't return null,
        /// does throw if CRCs don't match.
        /// </returns>
        public static EventStreamMessage FromBuffer(byte[] buffer, int offset, int length)
        {
            var currentOffset = offset;

            //get the total length of the message
            var totalLength = BitConverter.ToInt32(buffer, currentOffset);

            //endianness conversion
            totalLength    = EndianConversionUtility.NetworkToHostOrder(totalLength);
            currentOffset += SizeOfInt32;

            //get the length of the headers block.
            var headersLength = BitConverter.ToInt32(buffer, currentOffset);

            //endianness conversion
            headersLength  = EndianConversionUtility.NetworkToHostOrder(headersLength);
            currentOffset += SizeOfInt32;

            //get the prelude crc
            var preludeCrc = BitConverter.ToInt32(buffer, currentOffset);

            //endianness conversion
            preludeCrc = EndianConversionUtility.NetworkToHostOrder(preludeCrc);

            var message = new EventStreamMessage();

            message.Headers = new Dictionary <string, IEventStreamHeader>(StringComparer.Ordinal);

            using (var nullStream = new NullStream())
                using (var runningChecksum = new CrcCalculatorStream(nullStream))
                {
                    //write up to the prelude crc to the checksum stream
                    runningChecksum.Write(buffer, offset, currentOffset - offset);
                    //compare the current crc to the prelude crc and make sure they match.
                    if (preludeCrc != runningChecksum.Crc32)
                    {
                        throw new EventStreamChecksumFailureException(string.Format(CultureInfo.InvariantCulture, "Message Prelude Checksum failure. Expected {0} but was {1}", preludeCrc, runningChecksum.Crc32));
                    }

                    //if the data length passed isn't enough for the total length, that's an error condition.
                    if (totalLength != length)
                    {
                        throw new EventStreamChecksumFailureException(
                                  string.Format(CultureInfo.InvariantCulture, "Message Total Length didn't match the passed in length. Expected {0} but was {1}", length, totalLength));
                    }

                    //now write the prelude crc to the checksum stream
                    runningChecksum.Write(buffer, currentOffset, SizeOfInt32);
                    currentOffset += SizeOfInt32;

                    //prelude length is total message, minus framing and headers size.
                    var payloadLength = totalLength - headersLength - FramingSize;

                    //if we have headers, then loop over each one and parse them out.
                    if (headersLength > 0)
                    {
                        int preOpOffset = currentOffset;
                        while (currentOffset - PreludeLen < headersLength)
                        {
                            EventStreamHeader header = EventStreamHeader.FromBuffer(buffer, currentOffset, ref currentOffset);
                            message.Headers.Add(header.Name, header);
                        }

                        //after parsing the header remember to write that data to the checksum stream
                        runningChecksum.Write(buffer, preOpOffset, currentOffset - preOpOffset);
                    }

                    // now we're on the payload
                    message.Payload = new byte[payloadLength];
                    Buffer.BlockCopy(buffer, currentOffset, message.Payload, 0, message.Payload.Length);
                    runningChecksum.Write(buffer, currentOffset, message.Payload.Length);
                    currentOffset += message.Payload.Length;

                    //after reading the payload, get the message crc and make sure it matches.
                    var trailingCrc = BitConverter.ToInt32(buffer, currentOffset);
                    //endianness conversion.
                    trailingCrc = EndianConversionUtility.NetworkToHostOrder(trailingCrc);

                    if (trailingCrc != runningChecksum.Crc32)
                    {
                        throw new EventStreamChecksumFailureException(
                                  string.Format(CultureInfo.InvariantCulture, "Message Checksum failure. Expected {0} but was {1}", trailingCrc, runningChecksum.Crc32));
                    }
                }

            return(message);
        }