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); }
/// <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); }
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); }
/// <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); }
/// <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); }