/*
 * Copyright 2014-2018 Real Logic Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.aeron.driver.media;

import org.agrona.LangUtil;
import org.agrona.collections.ArrayUtil;
import org.agrona.concurrent.NanoClock;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;

public class MultiRcvDestination implements AutoCloseable
{
    private final long destinationTimeoutNs;
    private final NanoClock nanoClock;
    private ReceiveDestinationUdpTransport[] transports = new ReceiveDestinationUdpTransport[0];

    public MultiRcvDestination(final NanoClock nanoClock, final long timeoutNs)
    {
        this.nanoClock = nanoClock;
        this.destinationTimeoutNs = timeoutNs;
    }

    public void close()
    {
        for (final ReceiveDestinationUdpTransport transport : transports)
        {
            transport.close();
        }
    }

    public int addDestination(final ReceiveDestinationUdpTransport transport)
    {
        int index = transports.length;

        for (int i = 0, length = transports.length; i < length; i++)
        {
            if (null == transports[i])
            {
                index = i;
                break;
            }
        }

        transports = ArrayUtil.ensureCapacity(transports, index + 1);
        transports[index] = transport;

        return index;
    }

    public void removeDestination(final int transportIndex)
    {
        transports[transportIndex] = null;
    }

    public ReceiveDestinationUdpTransport transport(final int transportIndex)
    {
        return transports[transportIndex];
    }

    public int transport(final UdpChannel udpChannel)
    {
        final ReceiveDestinationUdpTransport[] transports = this.transports;
        int index = ArrayUtil.UNKNOWN_INDEX;

        for (int i = 0, length = transports.length; i < length; i++)
        {
            final ReceiveDestinationUdpTransport transport = transports[i];

            if (transport.udpChannel().equals(udpChannel))
            {
                index = i;
                break;
            }
        }

        return index;
    }

    public int sendToAll(
        final DestinationImageControlAddress[] controlAddresses,
        final ByteBuffer buffer,
        final int position,
        final int bytesToSend)
    {
        final ReceiveDestinationUdpTransport[] transports = this.transports;
        final long nowNs = nanoClock.nanoTime();
        int minBytesSent = bytesToSend;

        for (int lastIndex = controlAddresses.length - 1, i = lastIndex; i >= 0; i--)
        {
            final DestinationImageControlAddress controlAddress = controlAddresses[i];

            if (null != controlAddress)
            {
                final UdpChannelTransport transport = transports[i];

                if (null != transport && nowNs < (controlAddress.timeOfLastFrameNs + destinationTimeoutNs))
                {
                    buffer.position(position);
                    minBytesSent = Math.min(minBytesSent, sendTo(transport, buffer, controlAddress.address));
                }
            }
        }

        return minBytesSent;
    }

    public static int sendTo(
        final UdpChannelTransport transport, final ByteBuffer buffer, final InetSocketAddress remoteAddress)
    {
        int bytesSent = 0;
        try
        {
            if (null != transport && null != transport.sendDatagramChannel)
            {
                transport.sendHook(buffer, remoteAddress);
                bytesSent = transport.sendDatagramChannel.send(buffer, remoteAddress);
            }
        }
        catch (final IOException ex)
        {
            LangUtil.rethrowUnchecked(ex);
        }

        return bytesSent;
    }
}

@@ -887,16 +887,133 @@
 eable%0A%7B%0A
+    private static final ReceiveDestinationUdpTransport%5B%5D EMPTY_TRANSPORTS = new ReceiveDestinationUdpTransport%5B0%5D;%0A%0A
     priv
@@ -1150,45 +1150,24 @@
 s = 
-new ReceiveDestinationUdpTransport%5B0%5D
+EMPTY_TRANSPORTS
 ;%0A%0A 

Save initial allocation.