示例#1
0
文件: server.c 项目: foogywoo/drone
void *lo_server_recv_raw_stream(lo_server s, size_t *size)
{
    struct sockaddr_storage addr;
    socklen_t addr_len = sizeof(addr);
    char buffer[LO_MAX_MSG_SIZE];
    int32_t read_size;
    int ret;
    void *data = NULL;
    int sock;

#ifdef WIN32
    if(!initWSock()) return NULL;

    fd_set ps;
    FD_ZERO(&ps);
    FD_SET(s->socket,&ps);
    if(select(1,&ps,NULL,NULL,NULL) == SOCKET_ERROR)
        return NULL;
#else
    struct pollfd ps;
    ps.fd = s->socket;
    ps.events = POLLIN | POLLPRI;
    ps.revents = 0;
    poll(&ps, 1, -1);
#endif
    sock = accept(s->socket, (struct sockaddr *)&addr, &addr_len);

    ret = recv(sock, &read_size, sizeof(read_size), 0);
    read_size = ntohl(read_size);
    if (read_size > LO_MAX_MSG_SIZE) {
	close(sock);
	lo_throw(s, LO_TOOBIG, "Message too large", "recv()");

	return NULL;
    }
    ret = recv(sock, buffer, read_size, 0);
    //close(sock);
    if (ret <= 0) {
	return NULL;
    }
    data = malloc(ret);
    memcpy(data, buffer, ret);

    if (size) *size = ret;

    return data;
}
示例#2
0
文件: server.c 项目: foogywoo/drone
int lo_server_recv_noblock(lo_server s, int timeout)
{
    int sched_timeout = lo_server_next_event_delay(s) * 1000;
#ifdef WIN32
    fd_set ps;
    struct timeval stimeout;
#else
    struct pollfd ps;
#endif

#ifdef WIN32
    int res,to;

    if(!initWSock()) return 0;

    to = timeout > sched_timeout ? sched_timeout : timeout;
    stimeout.tv_sec = to/1000;
    stimeout.tv_usec = (to%1000)*1000;

    FD_ZERO(&ps);
    FD_SET(s->socket,&ps);
    res = select(1,&ps,NULL,NULL,&stimeout);

    if(res == SOCKET_ERROR)
        return 0;

    if (res || lo_server_next_event_delay(s) < 0.01)
	    return lo_server_recv(s);
#else
    ps.fd = s->socket;
    ps.events = POLLIN | POLLPRI | POLLERR | POLLHUP;
    ps.revents = 0;
    poll(&ps, 1, timeout > sched_timeout ? sched_timeout : timeout);

    if (ps.revents == POLLERR || ps.revents == POLLHUP) {
	return 0;
    }
    if (ps.revents || lo_server_next_event_delay(s) < 0.01) {
	return lo_server_recv(s);
    }
#endif

    return 0;
}
示例#3
0
文件: server.c 项目: foogywoo/drone
void *lo_server_recv_raw(lo_server s, size_t *size)
{
    char buffer[LO_MAX_MSG_SIZE];
    int ret;
    void *data = NULL;

#ifdef WIN32
    if(!initWSock()) return NULL;
#endif

    s->addr_len = sizeof(s->addr);

    ret = recvfrom(s->socket, buffer, LO_MAX_MSG_SIZE, 0,
		   (struct sockaddr *)&s->addr, &s->addr_len);
    if (ret <= 0) {
	return NULL;
    }
    data = malloc(ret);
    memcpy(data, buffer, ret);

    if (size) *size = ret;

    return data;
}
示例#4
0
static int send_data(lo_address a, lo_server from, char *data,
                     const size_t data_len)
{
    ssize_t ret = 0;
    int sock = -1;

#if defined(WIN32) || defined(_MSC_VER)
    if (!initWSock())
        return -1;
#endif

    if (data_len > LO_MAX_MSG_SIZE) {
        a->errnum = 99;
        a->errstr = "Attempted to send message in excess of maximum "
            "message size";
        return -1;
    }
    // Resolve the destination address, if not done already
    if (!a->ai) {
        ret = lo_address_resolve(a);
        if (ret)
            return ret;
    }
    // Re-use existing socket?
    if (from && a->protocol == LO_UDP) {
        sock = from->sockets[0].fd;
    } else if (a->protocol == LO_UDP && lo_client_sockets.udp != -1) {
        sock = lo_client_sockets.udp;
    } else {
        if (a->socket == -1) {
            ret = create_socket(a);
            if (ret)
                return ret;

            // If we are sending TCP, we may later receive on sending
            // socket, so add it to the from server's socket list.
            if (from && a->protocol == LO_TCP
                && (a->socket >= from->sources_len
                    || from->sources[a->socket].host == NULL))
            {
                lo_server_add_socket(from, a->socket, a, 0, 0);

                // If a socket is added to the server, the server is
                // now responsible for closing it.
                a->ownsocket = 0;
            }
        }
        sock = a->socket;
    }

    if (a->protocol == LO_TCP && !(a->flags & LO_SLIP)) {
        // For TCP only, send the length of the following data
        int32_t size = htonl(data_len);
        ret = send(sock, (const void*)&size, sizeof(size), MSG_NOSIGNAL);
    }
    // Send the data
    if (ret != -1) {
        if (a->protocol == LO_UDP) {
            struct addrinfo* ai;
            if (a->addr.size == sizeof(struct in_addr)) {
                setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
                           (const char*)&a->addr.a, a->addr.size);
            }
#ifdef ENABLE_IPV6
            else if (a->addr.size == sizeof(struct in6_addr)) {
                setsockopt(sock, IPPROTO_IP, IPV6_MULTICAST_IF,
                           (const char*)&a->addr.a, a->addr.size);
            }
#endif
            if (a->ttl >= 0) {
                unsigned char ttl = (unsigned char) a->ttl;
                setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
						   (const char*)&ttl, sizeof(ttl));
            }

            ai = a->ai;

            do {
                ret = sendto(sock, data, data_len, MSG_NOSIGNAL,
                             ai->ai_addr, ai->ai_addrlen);
                ai = ai->ai_next;
            } while (ret == -1 && ai != NULL);
            if (ret == -1 && ai != NULL && a->ai!=ai)
                a->ai = ai;
        } else {
            struct addrinfo* ai = a->ai;

            size_t len = data_len;
            if (a->flags & LO_SLIP)
                data = (char*)slip_encode((unsigned char*)data, &len);

            do {
                ret = send(sock, data, len, MSG_NOSIGNAL);
                if (a->protocol == LO_TCP)
                    ai = ai->ai_next;
                else
                    ai = 0;
            } while (ret == -1 && ai != NULL);
            if (ret == -1 && ai != NULL && a->ai!=ai)
                a->ai = ai;

            if (a->flags & LO_SLIP)
                free(data);
        }
    }

    if (ret == -1) {
        if (a->protocol == LO_TCP) {
            if (from)
                lo_server_del_socket(from, -1, a->socket);
            closesocket(a->socket);
            a->socket = -1;
        }

        a->errnum = geterror();
        a->errstr = NULL;
    } else {
        a->errnum = 0;
        a->errstr = NULL;
    }

    return ret;
}
示例#5
0
int send_data(lo_address a, lo_server from, char *data, const size_t data_len)
{
    int ret=0;
    int sock=-1;

#ifdef WIN32
    if(!initWSock()) return -1;
#endif

    if (data_len > LO_MAX_MSG_SIZE) {
	a->errnum = 99;
	a->errstr = "Attempted to send message in excess of maximum "
		    "message size";
	return -1;
    }
    
    // Resolve the destination address, if not done already
    if (!a->ai) {
	ret = resolve_address( a );
	if (ret) return ret;
    }

    // Re-use existing socket?
    if (from) {
	sock = from->sockets[0].fd;
    } else if (a->protocol == LO_UDP && lo_client_sockets.udp!=-1) {
	sock = lo_client_sockets.udp;
    } else {
	if (a->socket==-1) {
	    ret = create_socket( a );
	    if (ret) return ret;
    	}
	sock = a->socket;
    }



    // Send Length of the following data
    if (a->protocol == LO_TCP) {
	int32_t size = htonl(data_len); 
	ret = send(sock, &size, sizeof(size), MSG_NOSIGNAL); 
    }
    
    // Send the data
    if (a->protocol == LO_UDP) {
        if (a->ttl >= 0) {
            unsigned char ttl = (unsigned char)a->ttl;
            setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));
        }
        ret = sendto(sock, data, data_len, MSG_NOSIGNAL,
                     a->ai->ai_addr, a->ai->ai_addrlen);
    } else {
		ret = send(sock, data, data_len, MSG_NOSIGNAL);
    }

    if (a->protocol == LO_TCP && ret == -1) {
        close(a->socket);
        a->socket=-1;
    }
	
    if (ret == -1) {
		a->errnum = geterror();
		a->errstr = NULL;
    } else {
		a->errnum = 0;
		a->errstr = NULL;
    }

    return ret;
}
示例#6
0
文件: server.c 项目: foogywoo/drone
int lo_server_recv(lo_server s)
{
    void *data;
    size_t size;
    char *path;
    char *types;
    double sched_time = lo_server_next_event_delay(s);
#ifdef WIN32
    fd_set ps;
    struct timeval stimeout;
    int res;
#else
    struct pollfd ps;
#endif

again:
    if (sched_time > 0.01) {
	if (sched_time > 10.0) {
	    sched_time = 10.0;
	}

#ifdef WIN32
    if(!initWSock()) return 0;

    ps.fd_count = 1;
    ps.fd_array[0] = s->socket;

    stimeout.tv_sec = sched_time;
    stimeout.tv_usec = (sched_time-stimeout.tv_sec)*1.e6;
	res = select(1,&ps,NULL,NULL,&stimeout);
	if(res == SOCKET_ERROR) {
	    return 0;
	}

	if(!res) {
	    sched_time = lo_server_next_event_delay(s);

	    if (sched_time > 0.01) {
		goto again;
	    }

	    return dispatch_queued(s);
	}
#else
	ps.fd = s->socket;
	ps.events = POLLIN | POLLPRI | POLLERR | POLLHUP;
	ps.revents = 0;
	poll(&ps, 1, (int)(sched_time * 1000.0));

	if (ps.revents == POLLERR || ps.revents == POLLHUP) {
	    return 0;
	}

	if (!ps.revents) {
	    sched_time = lo_server_next_event_delay(s);

	    if (sched_time > 0.01) {
		goto again;
	    }

	    return dispatch_queued(s);
	}
#endif
    } else {
	return dispatch_queued(s);
    }
    if (s->protocol == LO_TCP) {
	data = lo_server_recv_raw_stream(s, &size);
    } else {
	data = lo_server_recv_raw(s, &size);
    }

    if (!data) {
	return 0;
    }
    path = data;

    types = data + lo_strsize(path);
    if (!strcmp(path, "#bundle")) {
	char *pos = types;
	uint32_t len;
	lo_timetag ts, now;

	lo_timetag_now(&now);

	ts.sec = lo_otoh32(*((uint32_t *)pos));
	pos += 4;
	ts.frac = lo_otoh32(*((uint32_t *)pos));
	pos += 4;

	while (pos - (char *)data < size) {
	    len = lo_otoh32(*((uint32_t *)pos));
	    pos += 4;
	    /* test for immedaite dispatch */
	    if ((ts.sec == 0 && ts.frac == 1) ||
				lo_timetag_diff(ts, now) <= 0.0) {
		types = pos + lo_strsize(pos);
		dispatch_method(s, pos, types + 1, types + lo_strsize(types));
	    } else {
		queue_data(s, ts, pos, len);
	    }
	    pos += len;
	}

	free(data);

	return size;
    } else if (*types != ',') {
	lo_throw(s, LO_ENOTYPE, "Missing typetag", path);

	return -1;
    }

    dispatch_method(s, path, types+1, data);

    free(data);

    return size;
}
示例#7
0
文件: server.c 项目: foogywoo/drone
lo_server lo_server_new_with_proto(const char *port, int proto,
				   lo_err_handler err_h)
{
    lo_server s;
    struct addrinfo *ai = NULL, *it, *used;
    struct addrinfo hints;
    int ret = -1;
    int tries = 0;
    char pnum[16];
    const char *service;
    char hostname[LO_HOST_SIZE];

#ifdef WIN32
    if(!initWSock()) return NULL;
#endif
    
    s = calloc(1, sizeof(struct _lo_server));

    s->err_h = err_h;
    s->first = NULL;
    s->ai = NULL;
    s->hostname = NULL;
    s->protocol = proto;
    s->port = 0;
    s->path = NULL;
    s->queued = NULL;
    s->socket = -1;

    memset(&hints, 0, sizeof(hints));

    if (proto == LO_UDP) {
	hints.ai_socktype = SOCK_DGRAM;
    } else if (proto == LO_TCP) {
	hints.ai_socktype = SOCK_STREAM;
    } 
#ifndef WIN32
    else if (proto == LO_UNIX) {

	struct sockaddr_un sa;

	s->socket = socket(PF_UNIX, SOCK_DGRAM, 0);
	if (s->socket == -1) {
        int err = geterror();
	    used = NULL;
	    lo_throw(s, err, strerror(err), "socket()");
	    lo_server_free(s);

	    return NULL;
	}

	sa.sun_family = AF_UNIX;
	strncpy(sa.sun_path, port, 107);

	if ((ret = bind(s->socket, (struct sockaddr *)&sa, sizeof(sa))) < 0) {
        int err = geterror();      
	    lo_throw(s, err, strerror(err), "bind()");

	    lo_server_free(s);
	    return NULL;
	}

	s->path = strdup(port);

	return s;
    } 
#endif
    else {
	lo_throw(s, LO_UNKNOWNPROTO, "Unknown protocol", NULL);
	lo_server_free(s);

	return NULL;
    }

#ifdef DISABLE_IPV6
    hints.ai_family = PF_INET;
#else
    hints.ai_family = PF_UNSPEC;
#endif
    hints.ai_flags = AI_PASSIVE;

    if (!port) {
	service = pnum;
    } else {
	service = port;
    }
    do {
	if (!port) {
	    /* not a good way to get random numbers, but its not critical */
	    snprintf(pnum, 15, "%ld", 10000 + ((unsigned int)rand() +
		     time(NULL)) % 10000);
	}

	if (ai) {
	    freeaddrinfo(ai);
	}

	if ((ret = getaddrinfo(NULL, service, &hints, &ai))) {
	    lo_throw(s, ret, gai_strerror(ret), NULL);
	    freeaddrinfo(ai);

	    return NULL;
	}

	used = NULL;
	s->ai = ai;
	s->socket = -1;
	s->port = 0;

	for (it = ai; it && s->socket == -1; it = it->ai_next) {
	    used = it;
	    s->socket = socket(it->ai_family, hints.ai_socktype, 0);
	}
	if (s->socket == -1) {
        int err = geterror();
	    used = NULL;
	    lo_throw(s, err, strerror(err), "socket()");

	    lo_server_free(s);
	    return NULL;
	}

	if ((ret = bind(s->socket, used->ai_addr, used->ai_addrlen)) < 0) {
        int err = geterror();
	    if (err == EINVAL || err == EADDRINUSE) {
		used = NULL;

		continue;
	    }
	    lo_throw(s, err, strerror(err), "bind()");

	    lo_server_free(s);

	    return NULL;
	}
    } while (!used && tries++ < 16);

    if (proto == LO_TCP) {
	listen(s->socket, 8);
    }

    if (!used) {
	lo_throw(s, LO_NOPORT, "cannot find free port", NULL);

	lo_server_free(s);
	return NULL;
    }

    if (proto == LO_UDP) {
	lo_client_sockets.udp = s->socket;
    } else if (proto == LO_TCP) {
        lo_client_sockets.tcp = s->socket;
    }

    /* Try it the IPV6 friendly way first */
    hostname[0] = '\0';
    for (it = ai; it; it = it->ai_next) {
	if (getnameinfo(it->ai_addr, it->ai_addrlen, hostname,
			sizeof(hostname), NULL, 0, NI_NAMEREQD) == 0) {
	    break;
	}
    }

    /* check to make sure getnameinfo() didn't just set the hostname to "::".
       Needed on Darwin. */
    if (hostname[0] == ':') {
	hostname[0] = '\0';
    }

    /* Fallback to the oldschool (i.e. more reliable) way */
    if (!hostname[0]) {
	struct hostent *he;

	gethostname(hostname, sizeof(hostname));
	he = gethostbyname(hostname);
	if (he) {
	    strncpy(hostname, he->h_name, sizeof(hostname));
	}
    }

    /* soethings gone really wrong, just hope its local only */
    if (!hostname[0]) {
	strcpy(hostname, "localhost");
    }
    s->hostname = strdup(hostname);

    if (used->ai_family == PF_INET6) {
	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)used->ai_addr;

	s->port = htons(addr->sin6_port);
    } else if (used->ai_family == PF_INET) {
	struct sockaddr_in *addr = (struct sockaddr_in *)used->ai_addr;

	s->port = htons(addr->sin_port);
    } else {
	lo_throw(s, LO_UNKNOWNPROTO, "unknown protocol family", NULL);
	s->port = atoi(port);
    }

    return s;
}
示例#8
0
文件: server.c 项目: aknuds1/chuck
int lo_server_recv(lo_server s)
{
    void *data;
    size_t size;
    double sched_time = lo_server_next_event_delay(s);
    int i;
#ifdef HAVE_SELECT
#ifndef HAVE_POLL
    fd_set ps;
    struct timeval stimeout;
    int res,nfds=0;
#endif
#endif

again:
    if (sched_time > 0.01) {
	if (sched_time > 10.0) {
	    sched_time = 10.0;
	}

#ifdef HAVE_POLL
    for (i=0; i < s->sockets_len; i++) {
        s->sockets[i].events = POLLIN | POLLPRI | POLLERR | POLLHUP;
        s->sockets[i].revents = 0;
    }

	poll(s->sockets, s->sockets_len, (int)(sched_time * 1000.0));

    for (i=0; i < s->sockets_len; i++)
    {
        if (   s->sockets[i].revents == POLLERR
            || s->sockets[i].revents == POLLHUP)
            return 0;

        if (s->sockets[i].revents)
            break;
    }

    if (i >= s->sockets_len)
    {
        sched_time = lo_server_next_event_delay(s);

        if (sched_time > 0.01)
            goto again;

        return dispatch_queued(s);
    }
#else
#ifdef HAVE_SELECT
    if(!initWSock()) return 0;

    FD_ZERO(&ps);
    for (i=0; i < s->sockets_len; i++) {
        FD_SET(s->sockets[i].fd,&ps);
        if (s->sockets[i].fd > nfds)
            nfds = s->sockets[i].fd;
    }

    stimeout.tv_sec = sched_time;
    stimeout.tv_usec = (sched_time-stimeout.tv_sec)*1.e6;
	res = select(nfds+1,&ps,NULL,NULL,&stimeout);
	if(res == SOCKET_ERROR) {
	    return 0;
	}

	if(!res) {
	    sched_time = lo_server_next_event_delay(s);

	    if (sched_time > 0.01)
            goto again;

	    return dispatch_queued(s);
	}
#endif
#endif
    } else {
	return dispatch_queued(s);
    }
    if (s->protocol == LO_TCP) {
	data = lo_server_recv_raw_stream(s, &size);
    } else {
	data = lo_server_recv_raw(s, &size);
    }

    if (!data) {
        return 0;
    }
    if (lo_server_dispatch_data(s, data, size) < 0) {
        free(data);
        return -1;
    }
    free(data);
    return size;
}
示例#9
0
文件: server.c 项目: aknuds1/chuck
void *lo_server_recv_raw_stream(lo_server s, size_t *size)
{
    struct sockaddr_storage addr;
    socklen_t addr_len = sizeof(addr);
    char buffer[LO_MAX_MSG_SIZE];
    int32_t read_size;
    int ret=0, i;
    void *data = NULL;
    int sock = -1;
    int repeat = 1;
#ifdef HAVE_SELECT
#ifndef HAVE_POLL
    fd_set ps;
    int nfds=0;
#endif
#endif

    /* check sockets in reverse order so that already-open sockets
     * have priority.  this allows checking for closed sockets even
     * when new connections are being requested.  it also allows to
     * continue looping through the list of sockets after closing and
     * deleting a socket, since deleting sockets doesn't affect the
     * order of the array to the left of the index. */

#ifdef HAVE_POLL
    for (i=0; i < s->sockets_len; i++) {
        s->sockets[i].events = POLLIN | POLLPRI;
        s->sockets[i].revents = 0;
    }

    poll(s->sockets, s->sockets_len, -1);

    for (i=(s->sockets_len-1); i >= 0; --i) {
        if (s->sockets[i].revents == POLLERR
            || s->sockets[i].revents == POLLHUP)
        {
            if (i>0) {
                close(s->sockets[i].fd);
                lo_server_del_socket(s, i, s->sockets[i].fd);
                continue;
            }
            else
                return NULL;
        }
        if (s->sockets[i].revents) {
            sock = s->sockets[i].fd;

#else
#ifdef HAVE_SELECT
    if(!initWSock()) return NULL;

    FD_ZERO(&ps);
    for (i=(s->sockets_len-1); i >= 0; --i) {
        FD_SET(s->sockets[i].fd, &ps);
        if (s->sockets[i].fd > nfds)
            nfds = s->sockets[i].fd;
    }

    if (select(nfds+1,&ps,NULL,NULL,NULL) == SOCKET_ERROR)
        return NULL;

    for (i=0; i < s->sockets_len; i++) {
        if (FD_ISSET(s->sockets[i].fd, &ps)) {
            sock = s->sockets[i].fd;

#endif
#endif

    if (sock == -1 || !repeat)
        return NULL;

    /* zeroeth socket is listening for new connections */
    if (sock == s->sockets[0].fd) {
        sock = accept(sock, (struct sockaddr *)&addr, &addr_len);
        i = lo_server_add_socket(s, sock);

        /* only repeat this loop for sockets other than the listening
         * socket,  (otherwise i will be wrong next time around) */
        repeat = 0;
    }

    if (i<0) {
        close(sock);
        return NULL;
    }

    ret = recv(sock, &read_size, sizeof(read_size), 0);
    read_size = ntohl(read_size);
    if (read_size > LO_MAX_MSG_SIZE || ret <= 0) {
        close(sock);
        lo_server_del_socket(s, i, sock);
        if (ret > 0)
            lo_throw(s, LO_TOOBIG, "Message too large", "recv()");
        continue;
    }
    ret = recv(sock, buffer, read_size, 0);
    if (ret <= 0) {
        close(sock);
        lo_server_del_socket(s, i, sock);
        continue;
    }

    /* end of loop over sockets: successfully read data */
    break;
        }
    }

    data = malloc(ret);
    memcpy(data, buffer, ret);

    if (size) *size = ret;

    return data;
}

int lo_server_wait(lo_server s, int timeout)
{
    int sched_timeout = lo_server_next_event_delay(s) * 1000;
    int i;
#ifdef HAVE_SELECT
#ifndef HAVE_POLL
    fd_set ps;
    struct timeval stimeout;
#endif
#endif

#ifdef HAVE_POLL
    for (i=0; i < s->sockets_len; i++) {
        s->sockets[i].events = POLLIN | POLLPRI | POLLERR | POLLHUP;
        s->sockets[i].revents = 0;
    }

    poll(s->sockets, s->sockets_len,
         timeout > sched_timeout ? sched_timeout : timeout);

    if (lo_server_next_event_delay(s) < 0.01)
        return 1;

    for (i=0; i < s->sockets_len; i++) {
        if (s->sockets[i].revents == POLLERR
            || s->sockets[i].revents == POLLHUP)
            return 0;
        if (s->sockets[i].revents)
            return 1;
    }
#else
#ifdef HAVE_SELECT
    int res,to,nfds=0;

    if(!initWSock()) return 0;

    to = timeout > sched_timeout ? sched_timeout : timeout;
    stimeout.tv_sec = to/1000;
    stimeout.tv_usec = (to%1000)*1000;

    FD_ZERO(&ps);
    for (i=0; i < s->sockets_len; i++) {
        FD_SET(s->sockets[i].fd,&ps);
        if (s->sockets[i].fd > nfds)
            nfds = s->sockets[i].fd;
    }

    res = select(nfds+1,&ps,NULL,NULL,&stimeout);

    if(res == SOCKET_ERROR)
        return 0;

    if (res || lo_server_next_event_delay(s) < 0.01)
	    return 1;
#endif
#endif

    return 0;
}

int lo_server_recv_noblock(lo_server s, int timeout)
{
    int result = lo_server_wait(s,timeout);
    if (result>0) {
      return lo_server_recv(s);
    } else {
      return 0;
    }
}