ENDPOINT_HANDLE connection_create_endpoint()

in src/connection.c [1851:1938]


ENDPOINT_HANDLE connection_create_endpoint(CONNECTION_HANDLE connection)
{
    ENDPOINT_HANDLE result;

    /* Codes_S_R_S_CONNECTION_01_113: [If connection, on_endpoint_frame_received or on_connection_state_changed is NULL, connection_create_endpoint shall fail and return NULL.] */
    /* Codes_S_R_S_CONNECTION_01_193: [The context argument shall be allowed to be NULL.] */
    if (connection == NULL)
    {
        LogError("NULL connection");
        result = NULL;
    }
    else
    {
        /* Codes_S_R_S_CONNECTION_01_115: [If no more endpoints can be created due to all channels being used, connection_create_endpoint shall fail and return NULL.] */
        if (connection->endpoint_count >= connection->channel_max)
        {
            result = NULL;
        }
        else
        {
            uint32_t i = 0;

            /* Codes_S_R_S_CONNECTION_01_128: [The lowest number outgoing channel shall be associated with the newly created endpoint.] */
            for (i = 0; i < connection->endpoint_count; i++)
            {
                if (connection->endpoints[i]->outgoing_channel > i)
                {
                    /* found a gap in the sorted endpoint array */
                    break;
                }
            }

            /* Codes_S_R_S_CONNECTION_01_127: [On success, connection_create_endpoint shall return a non-NULL handle to the newly created endpoint.] */
            result = (ENDPOINT_HANDLE)calloc(1, sizeof(ENDPOINT_INSTANCE));
            /* Codes_S_R_S_CONNECTION_01_196: [If memory cannot be allocated for the new endpoint, connection_create_endpoint shall fail and return NULL.] */
            if (result == NULL)
            {
                LogError("Cannot allocate memory for endpoint");
            }
            else
            {
                ENDPOINT_HANDLE* new_endpoints;

                result->on_endpoint_frame_received = NULL;
                result->on_connection_state_changed = NULL;
                result->callback_context = NULL;
                result->outgoing_channel = (uint16_t)i;
                result->connection = connection;

                /* Codes_S_R_S_CONNECTION_01_197: [The newly created endpoint shall be added to the endpoints list, so that it can be tracked.] */
                size_t realloc_size = safe_add_size_t((size_t)connection->endpoint_count, 1);
                realloc_size = safe_multiply_size_t(realloc_size, sizeof(ENDPOINT_HANDLE));
                if (realloc_size == SIZE_MAX ||
                    (new_endpoints = (ENDPOINT_HANDLE*)realloc(connection->endpoints, realloc_size)) == NULL)
                {
                    /* Tests_S_R_S_CONNECTION_01_198: [If adding the endpoint to the endpoints list tracked by the connection fails, connection_create_endpoint shall fail and return NULL.] */
                    LogError("Cannot reallocate memory for connection endpoints, size:%zu", realloc_size);
                    free(result);
                    result = NULL;
                }
                else
                {
                    connection->endpoints = new_endpoints;

                    if (i < connection->endpoint_count)
                    {
                        size_t memmove_size = safe_multiply_size_t(safe_subtract_size_t(connection->endpoint_count, i), sizeof(ENDPOINT_INSTANCE*));
                        if (memmove_size != SIZE_MAX)
                        {
                            (void)memmove(&connection->endpoints[i + 1], &connection->endpoints[i], memmove_size);
                        }
                        else
                        {
                            LogError("Cannot memmove endpoints, size:%zu", memmove_size);
                        }
                    }

                    connection->endpoints[i] = result;
                    connection->endpoint_count++;

                    /* Codes_S_R_S_CONNECTION_01_112: [connection_create_endpoint shall create a new endpoint that can be used by a session.] */
                }
            }
        }
    }

    return result;
}