From: Michael Wallner Date: Fri, 18 Dec 2020 13:12:38 +0000 (+0100) Subject: consolidate X-Git-Tag: 1.1.0-beta1~31 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Flibmemcached;a=commitdiff_plain;h=7781e7d0bf9fc0d986308838a3e1e47d0df3c019 consolidate --- diff --git a/.gitignore b/.gitignore index 47dcabb5..e2a11407 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.output *.pop *.rej +*.bak *TAGS *patch .cproject diff --git a/src/libmemcached/CMakeLists.txt b/src/libmemcached/CMakeLists.txt index 51d2dd18..c688c7b9 100644 --- a/src/libmemcached/CMakeLists.txt +++ b/src/libmemcached/CMakeLists.txt @@ -28,7 +28,7 @@ set(LIBMEMCACHED_SOURCES ${FLEX_CSL_SCANNER_OUTPUTS} allocators.cc analyze.cc - array.c + array.cc auto.cc backtrace.cc behavior.cc @@ -72,7 +72,7 @@ set(LIBMEMCACHED_SOURCES udp.cc verbosity.cc version.cc - virtual_bucket.c) + virtual_bucket.cc) check_cxx_compiler_flag(-Wno-deprecated-register W_NO_DEPRECATED_REGISTER) diff --git a/src/libmemcached/array.c b/src/libmemcached/array.c deleted file mode 100644 index a93b3071..00000000 --- a/src/libmemcached/array.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | libmemcached - C/C++ Client Library for memcached | - +--------------------------------------------------------------------+ - | Redistribution and use in source and binary forms, with or without | - | modification, are permitted under the terms of the BSD license. | - | You should have received a copy of the license in a bundled file | - | named LICENSE; in case you did not receive a copy you can review | - | the terms online at: https://opensource.org/licenses/BSD-3-Clause | - +--------------------------------------------------------------------+ - | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ | - | Copyright (c) 2020 Michael Wallner | - +--------------------------------------------------------------------+ -*/ - -#include "libmemcached/common.h" -#include -#include - -struct memcached_array_st { - Memcached *root; - size_t size; - char c_str[]; -}; - -memcached_array_st *memcached_array_clone(Memcached *memc, const memcached_array_st *original) { - if (original) { - return memcached_strcpy(memc, original->c_str, original->size); - } - - return NULL; -} - -memcached_array_st *memcached_strcpy(Memcached *memc, const char *str, size_t str_length) { - assert(memc); - assert(str); - assert(str_length); - - memcached_array_st *array = (struct memcached_array_st *) libmemcached_malloc( - memc, sizeof(struct memcached_array_st) + str_length + 1); - - if (array) { - array->root = memc; - array->size = str_length; // We don't count the NULL ending - memcpy(array->c_str, str, str_length); - array->c_str[str_length] = 0; - } - - return array; -} - -bool memcached_array_is_null(memcached_array_st *array) { - if (array) { - return false; - } - - return true; -} - -memcached_string_t memcached_array_to_string(memcached_array_st *array) { - assert(array); - assert(array->c_str); - assert(array->size); - memcached_string_t tmp; - tmp.c_str = array->c_str; - tmp.size = array->size; - - return tmp; -} - -void memcached_array_free(memcached_array_st *array) { - if (array) { - WATCHPOINT_ASSERT(array->root); - libmemcached_free(array->root, array); - } -} - -size_t memcached_array_size(memcached_array_st *array) { - if (array) { - return array->size; - } - - return 0; -} - -const char *memcached_array_string(memcached_array_st *array) { - if (array) { - return array->c_str; - } - - return NULL; -} diff --git a/src/libmemcached/array.cc b/src/libmemcached/array.cc new file mode 100644 index 00000000..176005cf --- /dev/null +++ b/src/libmemcached/array.cc @@ -0,0 +1,92 @@ +/* + +--------------------------------------------------------------------+ + | libmemcached - C/C++ Client Library for memcached | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted under the terms of the BSD license. | + | You should have received a copy of the license in a bundled file | + | named LICENSE; in case you did not receive a copy you can review | + | the terms online at: https://opensource.org/licenses/BSD-3-Clause | + +--------------------------------------------------------------------+ + | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ | + | Copyright (c) 2020 Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#include "libmemcached/common.h" + +#include + +struct memcached_array_st { + Memcached *root; + size_t size; + char c_str[]; +}; + +memcached_array_st *memcached_array_clone(Memcached *memc, const memcached_array_st *original) { + if (original) { + return memcached_strcpy(memc, original->c_str, original->size); + } + + return NULL; +} + +memcached_array_st *memcached_strcpy(Memcached *memc, const char *str, size_t str_length) { + assert(memc); + assert(str); + assert(str_length); + + memcached_array_st *array = (struct memcached_array_st *) libmemcached_malloc( + memc, sizeof(struct memcached_array_st) + str_length + 1); + + if (array) { + array->root = memc; + array->size = str_length; // We don't count the NULL ending + memcpy(array->c_str, str, str_length); + array->c_str[str_length] = 0; + } + + return array; +} + +bool memcached_array_is_null(memcached_array_st *array) { + if (array) { + return false; + } + + return true; +} + +memcached_string_t memcached_array_to_string(memcached_array_st *array) { + assert(array); + assert(array->c_str); + assert(array->size); + memcached_string_t tmp; + tmp.c_str = array->c_str; + tmp.size = array->size; + + return tmp; +} + +void memcached_array_free(memcached_array_st *array) { + if (array) { + WATCHPOINT_ASSERT(array->root); + libmemcached_free(array->root, array); + } +} + +size_t memcached_array_size(memcached_array_st *array) { + if (array) { + return array->size; + } + + return 0; +} + +const char *memcached_array_string(memcached_array_st *array) { + if (array) { + return array->c_str; + } + + return NULL; +} diff --git a/src/libmemcached/virtual_bucket.c b/src/libmemcached/virtual_bucket.c deleted file mode 100644 index 40c3fad0..00000000 --- a/src/libmemcached/virtual_bucket.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | libmemcached - C/C++ Client Library for memcached | - +--------------------------------------------------------------------+ - | Redistribution and use in source and binary forms, with or without | - | modification, are permitted under the terms of the BSD license. | - | You should have received a copy of the license in a bundled file | - | named LICENSE; in case you did not receive a copy you can review | - | the terms online at: https://opensource.org/licenses/BSD-3-Clause | - +--------------------------------------------------------------------+ - | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ | - | Copyright (c) 2020 Michael Wallner | - +--------------------------------------------------------------------+ -*/ - -#include "libmemcached/common.h" - -struct bucket_t { - uint32_t master; - uint32_t forward; -}; - -struct memcached_virtual_bucket_t { - bool has_forward; - uint32_t size; - uint32_t replicas; - struct bucket_t buckets[]; -}; - -memcached_return_t memcached_virtual_bucket_create(memcached_st *self, const uint32_t *host_map, - const uint32_t *forward_map, - const uint32_t buckets, - const uint32_t replicas) { - if (self == NULL || host_map == NULL || buckets == 0U) { - return MEMCACHED_INVALID_ARGUMENTS; - } - - memcached_virtual_bucket_free(self); - - struct memcached_virtual_bucket_t *virtual_bucket = (struct memcached_virtual_bucket_t *) malloc( - sizeof(struct memcached_virtual_bucket_t) + sizeof(struct bucket_t) * buckets); - - if (virtual_bucket == NULL) { - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - } - - virtual_bucket->size = buckets; - virtual_bucket->replicas = replicas; - self->virtual_bucket = virtual_bucket; - - uint32_t x = 0; - for (; x < buckets; x++) { - virtual_bucket->buckets[x].master = host_map[x]; - if (forward_map) { - virtual_bucket->buckets[x].forward = forward_map[x]; - } else { - virtual_bucket->buckets[x].forward = 0; - } - } - - return MEMCACHED_SUCCESS; -} - -void memcached_virtual_bucket_free(memcached_st *self) { - if (self) { - if (self->virtual_bucket) { - free(self->virtual_bucket); - self->virtual_bucket = NULL; - } - } -} - -uint32_t memcached_virtual_bucket_get(const memcached_st *self, uint32_t digest) { - if (self) { - if (self->virtual_bucket) { - uint32_t result = (uint32_t)(digest & (self->virtual_bucket->size - 1)); - return self->virtual_bucket->buckets[result].master; - } - - return (uint32_t)(digest & (self->number_of_hosts - 1)); - } - - return 0; -} diff --git a/src/libmemcached/virtual_bucket.cc b/src/libmemcached/virtual_bucket.cc new file mode 100644 index 00000000..2c9a3dd3 --- /dev/null +++ b/src/libmemcached/virtual_bucket.cc @@ -0,0 +1,85 @@ +/* + +--------------------------------------------------------------------+ + | libmemcached - C/C++ Client Library for memcached | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted under the terms of the BSD license. | + | You should have received a copy of the license in a bundled file | + | named LICENSE; in case you did not receive a copy you can review | + | the terms online at: https://opensource.org/licenses/BSD-3-Clause | + +--------------------------------------------------------------------+ + | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ | + | Copyright (c) 2020 Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#include "libmemcached/common.h" +#include "libmemcached/virtual_bucket.h" + +struct bucket_t { + uint32_t master; + uint32_t forward; +}; + +struct memcached_virtual_bucket_t { + bool has_forward; + uint32_t size; + uint32_t replicas; + struct bucket_t buckets[]; +}; + +memcached_return_t memcached_virtual_bucket_create(memcached_st *self, const uint32_t *host_map, + const uint32_t *forward_map, + const uint32_t buckets, + const uint32_t replicas) { + if (self == NULL || host_map == NULL || buckets == 0U) { + return MEMCACHED_INVALID_ARGUMENTS; + } + + memcached_virtual_bucket_free(self); + + struct memcached_virtual_bucket_t *virtual_bucket = (struct memcached_virtual_bucket_t *) malloc( + sizeof(struct memcached_virtual_bucket_t) + sizeof(struct bucket_t) * buckets); + + if (virtual_bucket == NULL) { + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + } + + virtual_bucket->size = buckets; + virtual_bucket->replicas = replicas; + self->virtual_bucket = virtual_bucket; + + uint32_t x = 0; + for (; x < buckets; x++) { + virtual_bucket->buckets[x].master = host_map[x]; + if (forward_map) { + virtual_bucket->buckets[x].forward = forward_map[x]; + } else { + virtual_bucket->buckets[x].forward = 0; + } + } + + return MEMCACHED_SUCCESS; +} + +void memcached_virtual_bucket_free(memcached_st *self) { + if (self) { + if (self->virtual_bucket) { + free(self->virtual_bucket); + self->virtual_bucket = NULL; + } + } +} + +uint32_t memcached_virtual_bucket_get(const memcached_st *self, uint32_t digest) { + if (self) { + if (self->virtual_bucket) { + uint32_t result = (uint32_t)(digest & (self->virtual_bucket->size - 1)); + return self->virtual_bucket->buckets[result].master; + } + + return (uint32_t)(digest & (self->number_of_hosts - 1)); + } + + return 0; +} diff --git a/src/p9y/CMakeLists.txt b/src/p9y/CMakeLists.txt index 1734f0b7..9d953ad0 100644 --- a/src/p9y/CMakeLists.txt +++ b/src/p9y/CMakeLists.txt @@ -19,18 +19,3 @@ target_include_directories(p9y PRIVATE if(NOT HAVE_GETOPT_H) target_sources(p9y PRIVATE getopt.c) endif() -if(NOT HAVE_LIBGEN_H) - target_sources(p9y PRIVATE libgen.c) -endif() -if(NOT HAVE_REALPATH) - target_sources(p9y PRIVATE realpath.c) -endif() -if(WIN32) - target_sources(p9y PRIVATE socket.c) -endif() -if(NOT HAVE_POLL_H AND NOT HAVE_SYS_POLL_H AND NOT WIN32) - target_sources(p9y PRIVATE poll.c) -endif() -if(NOT HAVE_GETTIMEOFDAY) - target_sources(p9y PRIVATE gettimeofday.cpp) -endif() diff --git a/src/p9y/gettimeofday.cpp b/src/p9y/gettimeofday.cpp deleted file mode 100644 index 7d002ca8..00000000 --- a/src/p9y/gettimeofday.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "gettimeofday.hpp" - -#include - -#if !defined HAVE_GETTIMEOFDAY -int gettimeofday(struct timeval* tp, struct timezone*) { - using clock = std::chrono::system_clock; - auto as_sec = [] (auto d) { - return std::chrono::duration_cast(d); - }; - auto as_usec = [] (auto d) { - return std::chrono::duration_cast(d); - }; - - auto now = clock::now().time_since_epoch(); - auto sec = as_sec(now); - auto usec = as_usec(now - sec); - - tp->tv_sec = sec.count(); - tp->tv_usec = usec.count(); - return 0; -} -#endif diff --git a/src/p9y/gettimeofday.hpp b/src/p9y/gettimeofday.hpp index aaba58bb..93452b66 100644 --- a/src/p9y/gettimeofday.hpp +++ b/src/p9y/gettimeofday.hpp @@ -2,11 +2,26 @@ #include "libmemcached-1/platform.h" -#include +#if defined __cplusplus +# include +#else +# include +#endif + #if defined HAVE_SYS_TIME_H # include #endif #if !defined HAVE_GETTIMEOFDAY +# define P9Y_NEED_GETTIMEOFDAY +#endif + +#if defined P9Y_NEED_GETTIMEOFDAY +# if defined __cplusplus +extern "C" { +# endif int gettimeofday(struct timeval* tp, struct timezone* tzp); +# if defined __cplusplus +}; +#endif #endif diff --git a/src/p9y/libgen.c b/src/p9y/libgen.c deleted file mode 100644 index 70696eb7..00000000 --- a/src/p9y/libgen.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "libgen.hpp" - -#if defined _WIN32 -char *basename(const char *filename) { - static char base[_MAX_PATH * 2], ext[_MAX_PATH], *ptr; - (void) _splitpath_s(filename, NULL, 0, NULL, 0, base, _MAX_PATH, ext, _MAX_PATH); - strcat_s(base, _MAX_PATH * 2 - 1, ext); - return base; -} -#endif diff --git a/src/p9y/libgen.hpp b/src/p9y/libgen.hpp index 82976d7e..264c4624 100644 --- a/src/p9y/libgen.hpp +++ b/src/p9y/libgen.hpp @@ -1,3 +1,4 @@ +#pragma once #include "mem_config.h" @@ -10,11 +11,12 @@ #if defined HAVE_LIBGEN_H # include #elif defined _WIN32 -# if defined __cplusplus +# if defined __cplusplus extern "C" { -# endif +# endif +# define P9Y_NEED_BASENAME char *basename(const char *filename); -# if defined __cplusplus +# if defined __cplusplus } -# endif +# endif #endif // HAVE_LIBGEN_H diff --git a/src/p9y/p9y.cpp b/src/p9y/p9y.cpp index 35ee8f01..1c5c4ac9 100644 --- a/src/p9y/p9y.cpp +++ b/src/p9y/p9y.cpp @@ -1 +1,148 @@ -// empty stub +#include "libgen.hpp" +#if defined P9Y_NEED_BASENAME +# if defined _WIN32 +char *basename(const char *filename) { + static char base[_MAX_PATH * 2], ext[_MAX_PATH], *ptr; + (void) _splitpath_s(filename, NULL, 0, NULL, 0, base, _MAX_PATH, ext, _MAX_PATH); + strcat_s(base, _MAX_PATH * 2 - 1, ext); + return base; +} +# endif // _WIN32 +#endif // P9Y_NEED_BASENAME + +#include "realpath.hpp" +#if defined P9Y_NEED_REALPATH +# if defined _WIN32 +char *realpath(const char *path, char real[_MAX_PATH]) { + return _fullpath(real, path, _MAX_PATH); +} +# endif // _WIN32 +#endif // P9Y_NEED_REALPATH + +#include "gettimeofday.hpp" +#if defined P9Y_NEED_GETTIMEOFDAY +# include +int gettimeofday(struct timeval* tp, struct timezone*) { + using clock = std::chrono::system_clock; + auto as_sec = [] (auto d) { + return std::chrono::duration_cast(d); + }; + auto as_usec = [] (auto d) { + return std::chrono::duration_cast(d); + }; + + auto now = clock::now().time_since_epoch(); + auto sec = as_sec(now); + auto usec = as_usec(now - sec); + + tp->tv_sec = sec.count(); + tp->tv_usec = usec.count(); + return 0; +} +#endif // P9Y_NEED_GETTIMEOFDAY + +#include "socket.hpp" +#if defined P9Y_NEED_GET_SOCKET_ERRNO +# if defined _WIN32 +int get_socket_errno() { + int local_errno = WSAGetLastError(); + + switch (local_errno) { + case WSAEINVAL: + local_errno = EINPROGRESS; + break; + case WSAEALREADY: + case WSAEWOULDBLOCK: + local_errno = EAGAIN; + break; + + case WSAECONNREFUSED: + local_errno = ECONNREFUSED; + break; + + case WSAENETUNREACH: + local_errno = ENETUNREACH; + break; + + case WSAETIMEDOUT: + local_errno = ETIMEDOUT; + break; + + case WSAECONNRESET: + local_errno = ECONNRESET; + break; + + case WSAEADDRINUSE: + local_errno = EADDRINUSE; + break; + + case WSAEOPNOTSUPP: + local_errno = EOPNOTSUPP; + break; + + case WSAENOPROTOOPT: + local_errno = ENOPROTOOPT; + break; + + default: + break; + } + + return local_errno; +} +# endif // _WIN32 +#endif // P9Y_NEED_GET_SOCKET_ERRNO + +#include "poll.hpp" +#if defined P9Y_NEED_POLL +int poll(struct pollfd fds[], nfds_t nfds, int tmo) { + fd_set readfds, writefds, errorfds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&errorfds); + + int maxfd = 0; + + for (nfds_t x = 0; x < nfds; ++x) { + if (fds[x].events & (POLLIN | POLLOUT)) { +# ifndef _WIN32 + if (fds[x].fd > maxfd) { + maxfd = fds[x].fd; + } +# endif + if (fds[x].events & POLLIN) { + FD_SET(fds[x].fd, &readfds); + } + if (fds[x].events & POLLOUT) { + FD_SET(fds[x].fd, &writefds); + } + } + } + + struct timeval timeout = {tmo / 1000, (tmo % 1000) * 1000}; + struct timeval *tp = &timeout; + if (tmo == -1) { + tp = NULL; + } + int ret = select(maxfd + 1, &readfds, &writefds, &errorfds, tp); + if (ret <= 0) { + return ret; + } + + /* Iterate through all of them because I need to clear the revent map */ + for (nfds_t x = 0; x < nfds; ++x) { + fds[x].revents = 0; + if (FD_ISSET(fds[x].fd, &readfds)) { + fds[x].revents |= POLLIN; + } + if (FD_ISSET(fds[x].fd, &writefds)) { + fds[x].revents |= POLLOUT; + } + if (FD_ISSET(fds[x].fd, &errorfds)) { + fds[x].revents |= POLLERR; + } + } + + return ret; +} +#endif // P9Y_NEED_POLL diff --git a/src/p9y/poll.c b/src/p9y/poll.c deleted file mode 100644 index b745c8be..00000000 --- a/src/p9y/poll.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "poll.hpp" - -#if defined P9Y_NEED_POLL - -int poll(struct pollfd fds[], nfds_t nfds, int tmo) { - fd_set readfds, writefds, errorfds; - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&errorfds); - - int maxfd = 0; - - for (nfds_t x = 0; x < nfds; ++x) { - if (fds[x].events & (POLLIN | POLLOUT)) { -# ifndef _WIN32 - if (fds[x].fd > maxfd) { - maxfd = fds[x].fd; - } -# endif - if (fds[x].events & POLLIN) { - FD_SET(fds[x].fd, &readfds); - } - if (fds[x].events & POLLOUT) { - FD_SET(fds[x].fd, &writefds); - } - } - } - - struct timeval timeout = {tmo / 1000, (tmo % 1000) * 1000}; - struct timeval *tp = &timeout; - if (tmo == -1) { - tp = NULL; - } - int ret = select(maxfd + 1, &readfds, &writefds, &errorfds, tp); - if (ret <= 0) { - return ret; - } - - /* Iterate through all of them because I need to clear the revent map */ - for (nfds_t x = 0; x < nfds; ++x) { - fds[x].revents = 0; - if (FD_ISSET(fds[x].fd, &readfds)) { - fds[x].revents |= POLLIN; - } - if (FD_ISSET(fds[x].fd, &writefds)) { - fds[x].revents |= POLLOUT; - } - if (FD_ISSET(fds[x].fd, &errorfds)) { - fds[x].revents |= POLLERR; - } - } - - return ret; -} - -#endif // P9Y_NEED_POLL diff --git a/src/p9y/realpath.c b/src/p9y/realpath.c deleted file mode 100644 index cf6430a2..00000000 --- a/src/p9y/realpath.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "realpath.hpp" - -#if defined _WIN32 -char *realpath(const char *path, char real[_MAX_PATH]) { - return _fullpath(real, path, _MAX_PATH); -} -#endif diff --git a/src/p9y/realpath.hpp b/src/p9y/realpath.hpp index ed63cfac..04b6b308 100644 --- a/src/p9y/realpath.hpp +++ b/src/p9y/realpath.hpp @@ -9,13 +9,12 @@ #endif #if !defined HAVE_REALPATH -# if defined _WIN32 -# if defined __cplusplus +# define P9Y_NEED_REALPATH +# if defined __cplusplus extern "C" { -# endif +# endif char *realpath(const char *path, char real[_MAX_PATH]); -# if defined __cplusplus +# if defined __cplusplus } -# endif -# endif // _WIN32 +# endif #endif // HAVE_REALPATH diff --git a/src/p9y/socket.c b/src/p9y/socket.c deleted file mode 100644 index 1398e1d8..00000000 --- a/src/p9y/socket.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "socket.hpp" - -#if defined _WIN32 -int get_socket_errno() { - int local_errno = WSAGetLastError(); - - switch (local_errno) { - case WSAEINVAL: - local_errno = EINPROGRESS; - break; - case WSAEALREADY: - case WSAEWOULDBLOCK: - local_errno = EAGAIN; - break; - - case WSAECONNREFUSED: - local_errno = ECONNREFUSED; - break; - - case WSAENETUNREACH: - local_errno = ENETUNREACH; - break; - - case WSAETIMEDOUT: - local_errno = ETIMEDOUT; - break; - - case WSAECONNRESET: - local_errno = ECONNRESET; - break; - - case WSAEADDRINUSE: - local_errno = EADDRINUSE; - break; - - case WSAEOPNOTSUPP: - local_errno = EOPNOTSUPP; - break; - - case WSAENOPROTOOPT: - local_errno = ENOPROTOOPT; - break; - - default: - break; - } - - return local_errno; -} -#endif // _WIN32 diff --git a/src/p9y/socket.hpp b/src/p9y/socket.hpp index 23237e73..9678b39f 100644 --- a/src/p9y/socket.hpp +++ b/src/p9y/socket.hpp @@ -39,6 +39,7 @@ extern "C" { #endif # if defined _WIN32 +# define P9Y_NEED_GET_SOCKET_ERRNO int get_socket_errno(); # define SHUT_WR SD_SEND # define SHUT_RD SD_RECEIVE