Write() public method

Writes a block of bytes to the current stream using data read from buffer.
is null. /// or is less than 0 -or- /// and will exceed length. /// The stream is closed.
public Write ( byte buffer, int startIndex, int length ) : void
buffer byte The buffer to write data from.
startIndex int The byte offset in at which to begin writing from.
length int The maximum number of bytes to write.
return void
        public void Test()
        {
            MemoryStream ms = new MemoryStream();
            BlockAllocatedMemoryStream ms2 = new BlockAllocatedMemoryStream();

            for (int x = 0; x < 10000; x++)
            {
                int value = Random.Int32;
                ms.Write(value);
                ms.Write((byte)value);
                ms2.Write(value);
                ms2.Write((byte)value);
            }

            Compare(ms, ms2);
        }
        public void Test2()
        {
            MemoryStream ms = new MemoryStream();
            BlockAllocatedMemoryStream ms2 = new BlockAllocatedMemoryStream();

            for (int x = 0; x < 10000; x++)
            {
                int value = Random.Int32;
                ms.Write(value);
                ms2.Write(value);

                int seek = Random.Int16Between(-10, 20);
                if (ms.Position + seek < 0)
                {
                    seek = -seek;
                }
                ms.Position += seek;
                ms2.Position += seek;
            }

            Compare(ms, ms2);
        }
        private void ProcessBinaryMeasurements(IEnumerable<IBinaryMeasurement> measurements, long frameLevelTimestamp, bool useCompactMeasurementFormat, bool usePayloadCompression)
        {
            // Create working buffer
            using (BlockAllocatedMemoryStream workingBuffer = new BlockAllocatedMemoryStream())
            {
                // Serialize data packet flags into response
                DataPacketFlags flags = DataPacketFlags.Synchronized;

                if (m_compressionModes.HasFlag(CompressionModes.TSSC))
                {
                    flags |= DataPacketFlags.Compressed;
                }
                else
                {
                    if (useCompactMeasurementFormat)
                        flags |= DataPacketFlags.Compact;
                }

                workingBuffer.WriteByte((byte)flags);

                // Serialize frame timestamp into data packet - this only occurs in synchronized data packets,
                // unsynchronized subscriptions always include timestamps in the serialized measurements
                workingBuffer.Write(BigEndian.GetBytes(frameLevelTimestamp), 0, 8);

                // Serialize total number of measurement values to follow
                workingBuffer.Write(BigEndian.GetBytes(measurements.Count()), 0, 4);

                if (usePayloadCompression && m_compressionModes.HasFlag(CompressionModes.TSSC))
                {
                    if ((object)m_compressionBlock == null)
                        m_compressionBlock = new MeasurementCompressionBlock();
                    else
                        m_compressionBlock.Clear();

                    foreach (CompactMeasurement measurement in measurements.Cast<CompactMeasurement>())
                    {
                        if (m_compressionBlock.CanAddMeasurements)
                        {
                            m_compressionBlock.AddMeasurement(measurement.RuntimeID, measurement.Timestamp.Value, (uint)measurement.StateFlags, (float)measurement.AdjustedValue);
                        }
                        else
                        {
                            m_compressionBlock.CopyTo(workingBuffer);
                            m_compressionBlock.Clear();
                            m_compressionBlock.AddMeasurement(measurement.RuntimeID, measurement.Timestamp.Value, (uint)measurement.StateFlags, (float)measurement.AdjustedValue);
                        }
                    }

                    m_compressionBlock.CopyTo(workingBuffer);
                }
                else
                {
                    // Attempt compression when requested - encoding of compressed buffer only happens if size would be smaller than normal serialization
                    if (!usePayloadCompression || !measurements.Cast<CompactMeasurement>().CompressPayload(workingBuffer, m_compressionStrength, false, ref flags))
                    {
                        // Serialize measurements to data buffer
                        foreach (IBinaryMeasurement measurement in measurements)
                        {
                            measurement.CopyBinaryImageToStream(workingBuffer);
                        }
                    }

                    // Update data packet flags if it has updated compression flags
                    if ((flags & DataPacketFlags.Compressed) > 0)
                    {
                        workingBuffer.Seek(0, SeekOrigin.Begin);
                        workingBuffer.WriteByte((byte)flags);
                    }
                }

                // Publish data packet to client
                if ((object)m_parent != null)
                    m_parent.SendClientResponse(m_clientID, ServerResponse.DataPacket, ServerCommand.Subscribe, workingBuffer.ToArray());
            }
        }
        public void Test3()
        {
            MemoryStream ms = new MemoryStream();
            BlockAllocatedMemoryStream ms2 = new BlockAllocatedMemoryStream();

            for (int x = 0; x < 10000; x++)
            {
                long position = Random.Int64Between(0, 100000);
                ms.Position = position;
                ms2.Position = position;

                int value = Random.Int32;
                ms.Write(value);
                ms2.Write(value);

                long length = Random.Int64Between(100000 >> 1, 100000);
                ms.SetLength(length);
                ms2.SetLength(length);
            }

            Compare(ms, ms2);
        }
Example #5
0
        /// <summary>
        /// Sends a server command to the publisher connection.
        /// </summary>
        /// <param name="commandCode"><see cref="ServerCommand"/> to send.</param>
        /// <param name="data">Optional command data to send.</param>
        /// <returns><c>true</c> if <paramref name="commandCode"/> transmission was successful; otherwise <c>false</c>.</returns>
        public virtual bool SendServerCommand(ServerCommand commandCode, byte[] data = null)
        {
            if ((object)m_commandChannel != null && m_commandChannel.CurrentState == ClientState.Connected)
            {
                try
                {
                    using (BlockAllocatedMemoryStream commandPacket = new BlockAllocatedMemoryStream())
                    {
                        // Write command code into command packet
                        commandPacket.WriteByte((byte)commandCode);

                        // Write command buffer into command packet
                        if ((object)data != null && data.Length > 0)
                            commandPacket.Write(data, 0, data.Length);

                        // Send command packet to publisher
                        m_commandChannel.SendAsync(commandPacket.ToArray(), 0, (int)commandPacket.Length);
                        m_metadataRefreshPending = (commandCode == ServerCommand.MetaDataRefresh);
                    }

                    // Track server command in pending request queue
                    lock (m_requests)
                    {
                        // Make sure a pending request does not already exist
                        int index = m_requests.BinarySearch(commandCode);

                        if (index < 0)
                        {
                            // Add the new server command to the request list
                            m_requests.Add(commandCode);

                            // Make sure requests are sorted to allow for binary searching
                            m_requests.Sort();
                        }
                    }

                    return true;
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException($"Exception occurred while trying to send server command \"{commandCode}\" to publisher: {ex.Message}", ex));
                }
            }
            else
                OnProcessException(new InvalidOperationException($"Subscriber is currently unconnected. Cannot send server command \"{commandCode}\" to publisher."));

            return false;
        }
        public void Test4()
        {
            MemoryStream ms = new MemoryStream();
            BlockAllocatedMemoryStream ms2 = new BlockAllocatedMemoryStream();

            for (int x = 0; x < 10000; x++)
            {
                int value = Random.Int32;
                ms.Write(value);
                ms.Write((byte)value);
                ms2.Write(value);
                ms2.Write((byte)value);
            }

            for (int x = 0; x < 10000; x++)
            {
                long position = Random.Int64Between(0, ms.Length - 5);
                ms.Position = position;
                ms2.Position = position;

                if (ms.ReadInt32() != ms2.ReadInt32())
                    throw new Exception();
                if (ms.ReadNextByte() != ms2.ReadNextByte())
                    throw new Exception();
            }

            for (int x = 0; x < 10000; x++)
            {
                byte[] buffer1 = new byte[100];
                byte[] buffer2 = new byte[100];

                long position = Random.Int64Between(0, (long)(ms.Length * 1.1));
                int readLength = Random.Int32Between(0, 100);
                ms.Position = position;
                ms2.Position = position;

                if (ms.Read(buffer1, 99 - readLength, readLength) != ms2.Read(buffer2, 99 - readLength, readLength))
                {
                    CompareBytes(buffer1, buffer2);
                }

            }

            Compare(ms, ms2);
        }
Example #7
0
        /// <summary>
        /// Subscribes (or re-subscribes) to a data publisher for a set of data points.
        /// </summary>
        /// <param name="remotelySynchronized">Boolean value that determines if subscription should be remotely synchronized - note that data publisher may not allow remote synchronization.</param>
        /// <param name="compactFormat">Boolean value that determines if the compact measurement format should be used. Set to <c>false</c> for full fidelity measurement serialization; otherwise set to <c>true</c> for bandwidth conservation.</param>
        /// <param name="connectionString">Connection string that defines required and optional parameters for the subscription.</param>
        /// <returns><c>true</c> if subscribe transmission was successful; otherwise <c>false</c>.</returns>
        public virtual bool Subscribe(bool remotelySynchronized, bool compactFormat, string connectionString)
        {
            bool success = false;

            if (!string.IsNullOrWhiteSpace(connectionString))
            {
                try
                {
                    // Parse connection string to see if it contains a data channel definition
                    Dictionary<string, string> settings = connectionString.ParseKeyValuePairs();
                    UdpClient dataChannel = null;
                    string setting;

                    // Track specified time inclusion for later deserialization
                    if (settings.TryGetValue("includeTime", out setting))
                        m_includeTime = setting.ParseBoolean();
                    else
                        m_includeTime = true;

                    settings.TryGetValue("dataChannel", out setting);

                    if (!string.IsNullOrWhiteSpace(setting))
                    {
                        dataChannel = new UdpClient(setting);

                        dataChannel.ReceiveBufferSize = ushort.MaxValue;
                        dataChannel.MaxConnectionAttempts = -1;
                        dataChannel.ConnectAsync();
                    }

                    // Assign data channel client reference and attach to needed events
                    DataChannel = dataChannel;

                    // Setup subscription packet
                    using (BlockAllocatedMemoryStream buffer = new BlockAllocatedMemoryStream())
                    {
                        DataPacketFlags flags = DataPacketFlags.NoFlags;
                        byte[] bytes;

                        if (remotelySynchronized)
                            flags |= DataPacketFlags.Synchronized;

                        if (compactFormat)
                            flags |= DataPacketFlags.Compact;

                        // Write data packet flags into buffer
                        buffer.WriteByte((byte)flags);

                        // Get encoded bytes of connection string
                        bytes = m_encoding.GetBytes(connectionString);

                        // Write encoded connection string length into buffer
                        buffer.Write(BigEndian.GetBytes(bytes.Length), 0, 4);

                        // Encode connection string into buffer
                        buffer.Write(bytes, 0, bytes.Length);

                        // Cache subscribed synchronization state
                        m_synchronizedSubscription = remotelySynchronized;

                        // Send subscribe server command with associated command buffer
                        success = SendServerCommand(ServerCommand.Subscribe, buffer.ToArray());
                    }
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException("Exception occurred while trying to make publisher subscription: " + ex.Message, ex));
                }
            }
            else
                OnProcessException(new InvalidOperationException("Cannot make publisher subscription without a connection string."));

            // Reset decompressor on successful resubscription
            if (success && (object)m_decompressionBlock != null)
                m_decompressionBlock.Reset();

            return success;
        }
Example #8
0
        /// <summary>
        /// Sends a server command to the publisher connection with associated <paramref name="message"/> data.
        /// </summary>
        /// <param name="commandCode"><see cref="ServerCommand"/> to send.</param>
        /// <param name="message">String based command data to send to server.</param>
        /// <returns><c>true</c> if <paramref name="commandCode"/> transmission was successful; otherwise <c>false</c>.</returns>
        public virtual bool SendServerCommand(ServerCommand commandCode, string message)
        {
            if (!string.IsNullOrWhiteSpace(message))
            {
                using (BlockAllocatedMemoryStream buffer = new BlockAllocatedMemoryStream())
                {
                    byte[] bytes = m_encoding.GetBytes(message);

                    buffer.Write(BigEndian.GetBytes(bytes.Length), 0, 4);
                    buffer.Write(bytes, 0, bytes.Length);

                    return SendServerCommand(commandCode, buffer.ToArray());
                }
            }

            return SendServerCommand(commandCode);
        }
        /// <summary>
        /// Sends a server command to the publisher connection.
        /// </summary>
        /// <param name="commandCode"><see cref="ServerCommand"/> to send.</param>
        /// <param name="data">Optional command data to send.</param>
        /// <returns><c>true</c> if <paramref name="commandCode"/> transmission was successful; otherwise <c>false</c>.</returns>
        public virtual bool SendServerCommand(ServerCommand commandCode, byte[] data = null)
        {
            if ((object)m_commandChannel != null && m_commandChannel.CurrentState == ClientState.Connected)
            {
                try
                {
                    using (BlockAllocatedMemoryStream commandPacket = new BlockAllocatedMemoryStream())
                    {
                        // Write command code into command packet
                        commandPacket.WriteByte((byte)commandCode);

                        // Write command buffer into command packet
                        if ((object)data != null && data.Length > 0)
                            commandPacket.Write(data, 0, data.Length);

                        // Send command packet to publisher
                        m_commandChannel.SendAsync(commandPacket.ToArray(), 0, (int)commandPacket.Length);
                        m_metadataRefreshPending = commandCode == ServerCommand.MetaDataRefresh;
                    }

                    return true;
                }
                catch (Exception ex)
                {
                    OnProcessException(MessageLevel.Error, new InvalidOperationException($"Exception occurred while trying to send server command \"{commandCode}\" to publisher: {ex.Message}", ex));
                }
            }
            else
                OnProcessException(MessageLevel.Error, new InvalidOperationException($"Subscriber is currently unconnected. Cannot send server command \"{commandCode}\" to publisher."));

            return false;
        }
Example #10
0
        /// <summary>
        /// Authenticates subscriber to a data publisher.
        /// </summary>
        /// <param name="sharedSecret">Shared secret used to look up private crypto key and initialization vector.</param>
        /// <param name="authenticationID">Authentication ID that publisher will use to validate subscriber identity.</param>
        /// <returns><c>true</c> if authentication transmission was successful; otherwise <c>false</c>.</returns>
        public virtual bool Authenticate(string sharedSecret, string authenticationID)
        {
            if (!string.IsNullOrWhiteSpace(authenticationID))
            {
                try
                {
                    using (BlockAllocatedMemoryStream buffer = new BlockAllocatedMemoryStream())
                    {
                        byte[] salt = new byte[DataPublisher.CipherSaltLength];
                        byte[] bytes;

                        // Generate some random prefix data to make sure auth key transmission is always unique
                        Random.GetBytes(salt);

                        // Get encoded bytes of authentication key
                        bytes = salt.Combine(m_encoding.GetBytes(authenticationID));

                        // Encrypt authentication key
                        bytes = bytes.Encrypt(sharedSecret, CipherStrength.Aes256);

                        // Write encoded authentication key length into buffer
                        buffer.Write(BigEndian.GetBytes(bytes.Length), 0, 4);

                        // Encode encrypted authentication key into buffer
                        buffer.Write(bytes, 0, bytes.Length);

                        // Send authentication command to server with associated command buffer
                        return SendServerCommand(ServerCommand.Authenticate, buffer.ToArray());
                    }
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException("Exception occurred while trying to authenticate publisher subscription: " + ex.Message, ex));
                }
            }
            else
                OnProcessException(new InvalidOperationException("Cannot authenticate subscription without a connection string."));

            return false;
        }
Example #11
0
        /// <summary>
        /// Rotates or initializes the crypto keys for this <see cref="ClientConnection"/>.
        /// </summary>
        public bool RotateCipherKeys()
        {
            // Make sure at least a second has passed before next key rotation
            if ((DateTime.UtcNow.Ticks - m_lastCipherKeyUpdateTime).ToMilliseconds() >= 1000.0D)
            {
                try
                {
                    // Since this function cannot be not called more than once per second there
                    // is no real benefit to maintaining these memory streams at a member level
                    using (BlockAllocatedMemoryStream response = new BlockAllocatedMemoryStream())
                    {
                        byte[] bytes, bufferLen;

                        // Create or update cipher keys and initialization vectors 
                        UpdateKeyIVs();

                        // Add current cipher index to response
                        response.WriteByte((byte)m_cipherIndex);

                        // Serialize new keys
                        using (BlockAllocatedMemoryStream buffer = new BlockAllocatedMemoryStream())
                        {
                            // Write even key
                            bufferLen = BigEndian.GetBytes(m_keyIVs[EvenKey][KeyIndex].Length);
                            buffer.Write(bufferLen, 0, bufferLen.Length);
                            buffer.Write(m_keyIVs[EvenKey][KeyIndex], 0, m_keyIVs[EvenKey][KeyIndex].Length);

                            // Write even initialization vector
                            bufferLen = BigEndian.GetBytes(m_keyIVs[EvenKey][IVIndex].Length);
                            buffer.Write(bufferLen, 0, bufferLen.Length);
                            buffer.Write(m_keyIVs[EvenKey][IVIndex], 0, m_keyIVs[EvenKey][IVIndex].Length);

                            // Write odd key
                            bufferLen = BigEndian.GetBytes(m_keyIVs[OddKey][KeyIndex].Length);
                            buffer.Write(bufferLen, 0, bufferLen.Length);
                            buffer.Write(m_keyIVs[OddKey][KeyIndex], 0, m_keyIVs[OddKey][KeyIndex].Length);

                            // Write odd initialization vector
                            bufferLen = BigEndian.GetBytes(m_keyIVs[OddKey][IVIndex].Length);
                            buffer.Write(bufferLen, 0, bufferLen.Length);
                            buffer.Write(m_keyIVs[OddKey][IVIndex], 0, m_keyIVs[OddKey][IVIndex].Length);

                            // Get bytes from serialized buffer
                            bytes = buffer.ToArray();
                        }

                        // Encrypt keys using private keys known only to current client and server
                        if (m_authenticated && !string.IsNullOrWhiteSpace(m_sharedSecret))
                            bytes = bytes.Encrypt(m_sharedSecret, CipherStrength.Aes256);

                        // Add serialized key response
                        response.Write(bytes, 0, bytes.Length);

                        // Send cipher key updates
                        m_parent.SendClientResponse(m_clientID, ServerResponse.UpdateCipherKeys, ServerCommand.Subscribe, response.ToArray());
                    }

                    // Send success message
                    m_parent.SendClientResponse(m_clientID, ServerResponse.Succeeded, ServerCommand.RotateCipherKeys, "New cipher keys established.");
                    m_parent.OnStatusMessage(ConnectionID + " cipher keys rotated.");
                    return true;
                }
                catch (Exception ex)
                {
                    // Send failure message
                    m_parent.SendClientResponse(m_clientID, ServerResponse.Failed, ServerCommand.RotateCipherKeys, "Failed to establish new cipher keys: " + ex.Message);
                    m_parent.OnStatusMessage("Failed to establish new cipher keys for {0}: {1}", ConnectionID, ex.Message);
                    return false;
                }
            }

            m_parent.SendClientResponse(m_clientID, ServerResponse.Failed, ServerCommand.RotateCipherKeys, "Cipher key rotation skipped, keys were already rotated within last second.");
            m_parent.OnStatusMessage("WARNING: Cipher key rotation skipped for {0}, keys were already rotated within last second.", ConnectionID);
            return false;
        }
Example #12
0
        private TransportProvider<EndPoint> AddUdpClient(EndPoint udpClientEndPoint)
        {
            TransportProvider<EndPoint> udpClient = new TransportProvider<EndPoint>();
            IPEndPoint udpClientIPEndPoint = udpClientEndPoint as IPEndPoint;
            UdpClientInfo udpClientInfo;

            // Set up client
            udpClient.SetReceiveBuffer(ReceiveBufferSize);
            udpClient.SetSendBuffer(SendBufferSize);
            udpClient.Provider = udpClientIPEndPoint;

            // If the IP specified for the client is a multicast IP, subscribe to the specified multicast group.
            if ((object)udpClientIPEndPoint != null && Transport.IsMulticastIP(udpClientIPEndPoint.Address))
            {
                SocketOptionLevel level = udpClientIPEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP;
                string multicastSource;

                if (m_configData.TryGetValue("multicastSource", out multicastSource))
                {
                    IPAddress sourceAddress = IPAddress.Parse(multicastSource);
                    IPAddress localAddress = (udpClientIPEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any);

                    if (sourceAddress.AddressFamily != udpClientIPEndPoint.AddressFamily)
                        throw new InvalidOperationException($"Source address \"{sourceAddress}\" is not in the same IP format as server address \"{udpClientIPEndPoint.Address}\"");

                    if (localAddress.AddressFamily != udpClientIPEndPoint.AddressFamily)
                        throw new InvalidOperationException($"Local address \"{localAddress}\" is not in the same IP format as server address \"{udpClientIPEndPoint.Address}\"");

                    using (BlockAllocatedMemoryStream membershipAddresses = new BlockAllocatedMemoryStream())
                    {
                        byte[] serverAddressBytes = udpClientIPEndPoint.Address.GetAddressBytes();
                        byte[] sourceAddressBytes = sourceAddress.GetAddressBytes();
                        byte[] localAddressBytes = localAddress.GetAddressBytes();

                        membershipAddresses.Write(serverAddressBytes, 0, serverAddressBytes.Length);
                        membershipAddresses.Write(sourceAddressBytes, 0, sourceAddressBytes.Length);
                        membershipAddresses.Write(localAddressBytes, 0, localAddressBytes.Length);

                        udpClient.MulticastMembershipAddresses = membershipAddresses.ToArray();
                    }

                    // Execute multicast subscribe for specific source
                    m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddSourceMembership, udpClient.MulticastMembershipAddresses);
                    m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"]));
                }
                else
                {
                    // Execute multicast subscribe for any source
                    m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddMembership, new MulticastOption(udpClientIPEndPoint.Address));
                    m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"]));
                }
            }

            // Create client info object
            udpClientInfo = new UdpClientInfo
            {
                Client = udpClient,
                SendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(),
                SendLock = new object(),
                SendQueue = new ConcurrentQueue<UdpServerPayload>()
            };

            udpClientInfo.DumpPayloadsOperation = new ShortSynchronizedOperation(() =>
            {
                UdpServerPayload payload;

                // Check to see if the client has reached the maximum send queue size.
                if (m_maxSendQueueSize > 0 && udpClientInfo.SendQueue.Count >= m_maxSendQueueSize)
                {
                    for (int i = 0; i < m_maxSendQueueSize; i++)
                    {
                        if (udpClientInfo.SendQueue.TryDequeue(out payload))
                        {
                            payload.WaitHandle.Set();
                            payload.WaitHandle.Dispose();
                            payload.WaitHandle = null;
                        }
                    }

                    throw new InvalidOperationException($"Client {udpClientInfo.Client.ID} connected to UDP server reached maximum send queue size. {m_maxSendQueueSize} payloads dumped from the queue.");
                }
            }, ex => OnSendClientDataException(udpClientInfo.Client.ID, ex));

            // Set up SocketAsyncEventArgs
            udpClientInfo.SendArgs.RemoteEndPoint = udpClient.Provider;
            udpClientInfo.SendArgs.SetBuffer(udpClient.SendBuffer, 0, udpClient.SendBufferSize);
            udpClientInfo.SendArgs.Completed += m_sendHandler;

            // Add new client to the lookup
            m_clientInfoLookup.TryAdd(udpClient.ID, udpClientInfo);
            OnClientConnected(udpClient.ID);

            return udpClient;
        }
Example #13
0
        /// <summary>
        /// Attempts to compress payload of <see cref="CompactMeasurement"/> values onto the <paramref name="destination"/> stream.
        /// </summary>
        /// <param name="compactMeasurements">Payload of <see cref="CompactMeasurement"/> values.</param>
        /// <param name="destination">Memory based <paramref name="destination"/> stream to hold compressed payload.</param>
        /// <param name="compressionStrength">Compression strength to use.</param>
        /// <param name="includeTime">Flag that determines if time should be included in the compressed payload.</param>
        /// <param name="flags">Current <see cref="DataPacketFlags"/>.</param>
        /// <returns><c>true</c> if payload was compressed and encoded onto <paramref name="destination"/> stream; otherwise <c>false</c>.</returns>
        /// <remarks>
        /// <para>
        /// Compressed payload will only be encoded onto <paramref name="destination"/> stream if compressed size would be smaller
        /// than normal serialized size.
        /// </para>
        /// <para>
        /// As an optimization this function uses a compression method that uses pointers to native structures, as such the
        /// endian order encoding of the compressed data will always be in the native-endian order of the operating system.
        /// This will be an important consideration when writing a endian order neutral payload decompressor. To help with
        /// this the actual endian order used during compression is marked in the data flags. However, measurements values
        /// are consistently encoded in big-endian order prior to buffer compression.
        /// </para>
        /// </remarks>
        public static bool CompressPayload(this IEnumerable<CompactMeasurement> compactMeasurements, BlockAllocatedMemoryStream destination, byte compressionStrength, bool includeTime, ref DataPacketFlags flags)
        {
            // Instantiate a buffer that is larger than we'll need
            byte[] buffer = new byte[ushort.MaxValue];

            // Go ahead an enumerate all the measurements - this will cast all values to compact measurements
            CompactMeasurement[] measurements = compactMeasurements.ToArray();
            int measurementCount = measurements.Length;
            int sizeToBeat = measurementCount * measurements[0].BinaryLength;
            int index = 0;

            // Encode compact state flags and runtime IDs together --
            // Together these are three bytes, so we pad with a zero byte.
            // The zero byte and state flags are considered to be more compressible
            // than the runtime ID, so these are stored in the higher order bytes.
            for (int i = 0; i < measurementCount; i++)
            {
                uint value = ((uint)measurements[i].CompactStateFlags << 16) | measurements[i].RuntimeID;
                index += NativeEndianOrder.Default.CopyBytes(value, buffer, index);
            }

            // Encode values
            for (int i = 0; i < measurementCount; i++)
            {
                // Encode using adjusted value (accounts for adder and multiplier)
                index += NativeEndianOrder.Default.CopyBytes((float)measurements[i].AdjustedValue, buffer, index);
            }

            if (includeTime)
            {
                // Encode timestamps
                for (int i = 0; i < measurementCount; i++)
                {
                    // Since large majority of 8-byte tick values will be repeated, they should compress well
                    index += NativeEndianOrder.Default.CopyBytes((long)measurements[i].Timestamp, buffer, index);
                }
            }

            // Attempt to compress buffer
            int compressedSize = PatternCompressor.CompressBuffer(buffer, 0, index, ushort.MaxValue, compressionStrength);

            // Only encode compressed buffer if compression actually helped payload size
            if (compressedSize <= sizeToBeat)
            {
                // Set payload compression flag
                flags |= DataPacketFlags.Compressed;

                // Make sure decompressor knows original endian encoding order
                if (BitConverter.IsLittleEndian)
                    flags |= DataPacketFlags.LittleEndianCompression;
                else
                    flags &= ~DataPacketFlags.LittleEndianCompression;

                // Copy compressed payload onto destination stream
                destination.Write(buffer, 0, compressedSize);
                return true;
            }

            // Clear payload compression flag
            flags &= ~DataPacketFlags.Compressed;
            return false;
        }
        // Rotates base time offsets
        private void RotateBaseTimes()
        {
            if ((object)m_parent != null && (object)m_baseTimeRotationTimer != null)
            {
                if ((object)m_baseTimeOffsets == null)
                {
                    m_baseTimeOffsets = new long[2];
                    m_baseTimeOffsets[0] = RealTime;
                    m_baseTimeOffsets[1] = RealTime + (long)m_baseTimeRotationTimer.Interval * Ticks.PerMillisecond;
                    m_timeIndex = 0;
                }
                else
                {
                    int oldIndex = m_timeIndex;

                    // Switch to newer timestamp
                    m_timeIndex ^= 1;

                    // Now make older timestamp the newer timestamp
                    m_baseTimeOffsets[oldIndex] = RealTime + (long)m_baseTimeRotationTimer.Interval * Ticks.PerMillisecond;
                }

                // Since this function will only be called periodically, there is no real benefit
                // to maintaining this memory stream at a member level
                using (BlockAllocatedMemoryStream responsePacket = new BlockAllocatedMemoryStream())
                {
                    responsePacket.Write(BigEndian.GetBytes(m_timeIndex), 0, 4);
                    responsePacket.Write(BigEndian.GetBytes(m_baseTimeOffsets[0]), 0, 8);
                    responsePacket.Write(BigEndian.GetBytes(m_baseTimeOffsets[1]), 0, 8);

                    m_parent.SendClientResponse(m_clientID, ServerResponse.UpdateBaseTimes, ServerCommand.Subscribe, responsePacket.ToArray());
                }
            }
        }
        private void ProcessBinaryMeasurements(IEnumerable<IBinaryMeasurement> measurements, bool useCompactMeasurementFormat, bool usePayloadCompression)
        {
            // Create working buffer
            using (BlockAllocatedMemoryStream workingBuffer = new BlockAllocatedMemoryStream())
            {
                // Serialize data packet flags into response
                DataPacketFlags flags = DataPacketFlags.NoFlags; // No flags means bit is cleared, i.e., unsynchronized

                if (useCompactMeasurementFormat)
                    flags |= DataPacketFlags.Compact;

                workingBuffer.WriteByte((byte)flags);

                // No frame level timestamp is serialized into the data packet since all data is unsynchronized and essentially
                // published upon receipt, however timestamps are optionally included in the serialized measurements.

                // Serialize total number of measurement values to follow
                workingBuffer.Write(BigEndian.GetBytes(measurements.Count()), 0, 4);

                // Attempt compression when requested - encoding of compressed buffer only happens if size would be smaller than normal serialization
                if (!usePayloadCompression || !measurements.Cast<CompactMeasurement>().CompressPayload(workingBuffer, m_compressionStrength, m_includeTime, ref flags))
                {
                    // Serialize measurements to data buffer
                    foreach (IBinaryMeasurement measurement in measurements)
                        measurement.CopyBinaryImageToStream(workingBuffer);
                }

                // Update data packet flags if it has updated compression flags
                if ((flags & DataPacketFlags.Compressed) > 0)
                {
                    workingBuffer.Seek(0, SeekOrigin.Begin);
                    workingBuffer.WriteByte((byte)flags);
                }

                // Publish data packet to client
                if ((object)m_parent != null)
                    m_parent.SendClientResponse(m_clientID, ServerResponse.DataPacket, ServerCommand.Subscribe, workingBuffer.ToArray());

                // Track last publication time
                m_lastPublishTime = DateTime.UtcNow.Ticks;
            }
        }