Squashed 'third_party/allwpilib_2019/' content from commit bd05dfa1c
Change-Id: I2b1c2250cdb9b055133780c33593292098c375b7
git-subtree-dir: third_party/allwpilib_2019
git-subtree-split: bd05dfa1c7cca74c4fac451e7b9d6a37e7b53447
diff --git a/wpiutil/src/main/native/cpp/TCPStream.cpp b/wpiutil/src/main/native/cpp/TCPStream.cpp
new file mode 100644
index 0000000..d4e3671
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/TCPStream.cpp
@@ -0,0 +1,209 @@
+/*
+ TCPStream.h
+
+ TCPStream class definition. TCPStream provides methods to trasnfer
+ data between peers over a TCP/IP connection.
+
+ ------------------------------------------
+
+ Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
+
+ 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.
+*/
+
+#include "wpi/TCPStream.h"
+
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#endif
+
+#include <cerrno>
+
+using namespace wpi;
+
+TCPStream::TCPStream(int sd, sockaddr_in* address)
+ : m_sd(sd), m_blocking(true) {
+ char ip[50];
+#ifdef _WIN32
+ InetNtop(PF_INET, &(address->sin_addr.s_addr), ip, sizeof(ip) - 1);
+#else
+ inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(address->sin_addr.s_addr)),
+ ip, sizeof(ip) - 1);
+#ifdef SO_NOSIGPIPE
+ // disable SIGPIPE on Mac OS X
+ int set = 1;
+ setsockopt(m_sd, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&set),
+ sizeof set);
+#endif
+#endif
+ m_peerIP = ip;
+ m_peerPort = ntohs(address->sin_port);
+}
+
+TCPStream::~TCPStream() { close(); }
+
+size_t TCPStream::send(const char* buffer, size_t len, Error* err) {
+ if (m_sd < 0) {
+ *err = kConnectionClosed;
+ return 0;
+ }
+#ifdef _WIN32
+ WSABUF wsaBuf;
+ wsaBuf.buf = const_cast<char*>(buffer);
+ wsaBuf.len = (ULONG)len;
+ DWORD rv;
+ bool result = true;
+ while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+ result = false;
+ break;
+ }
+ if (!m_blocking) {
+ *err = kWouldBlock;
+ return 0;
+ }
+ Sleep(1);
+ }
+ if (!result) {
+ char Buffer[128];
+#ifdef _MSC_VER
+ sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
+#else
+ std::snprintf(Buffer, sizeof(Buffer), "Send() failed: WSA error=%d\n",
+ WSAGetLastError());
+#endif
+ OutputDebugStringA(Buffer);
+ *err = kConnectionReset;
+ return 0;
+ }
+#else
+#ifdef MSG_NOSIGNAL
+ // disable SIGPIPE on Linux
+ ssize_t rv = ::send(m_sd, buffer, len, MSG_NOSIGNAL);
+#else
+ ssize_t rv = ::send(m_sd, buffer, len, 0);
+#endif
+ if (rv < 0) {
+ if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
+ *err = kWouldBlock;
+ else
+ *err = kConnectionReset;
+ return 0;
+ }
+#endif
+ return static_cast<size_t>(rv);
+}
+
+size_t TCPStream::receive(char* buffer, size_t len, Error* err, int timeout) {
+ if (m_sd < 0) {
+ *err = kConnectionClosed;
+ return 0;
+ }
+#ifdef _WIN32
+ int rv;
+#else
+ ssize_t rv;
+#endif
+ if (timeout <= 0) {
+#ifdef _WIN32
+ rv = recv(m_sd, buffer, len, 0);
+#else
+ rv = read(m_sd, buffer, len);
+#endif
+ } else if (WaitForReadEvent(timeout)) {
+#ifdef _WIN32
+ rv = recv(m_sd, buffer, len, 0);
+#else
+ rv = read(m_sd, buffer, len);
+#endif
+ } else {
+ *err = kConnectionTimedOut;
+ return 0;
+ }
+ if (rv < 0) {
+#ifdef _WIN32
+ if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+ if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
+#endif
+ *err = kWouldBlock;
+ else
+ *err = kConnectionReset;
+ return 0;
+ }
+ return static_cast<size_t>(rv);
+}
+
+void TCPStream::close() {
+ if (m_sd >= 0) {
+#ifdef _WIN32
+ ::shutdown(m_sd, SD_BOTH);
+ closesocket(m_sd);
+#else
+ ::shutdown(m_sd, SHUT_RDWR);
+ ::close(m_sd);
+#endif
+ }
+ m_sd = -1;
+}
+
+StringRef TCPStream::getPeerIP() const { return m_peerIP; }
+
+int TCPStream::getPeerPort() const { return m_peerPort; }
+
+void TCPStream::setNoDelay() {
+ if (m_sd < 0) return;
+ int optval = 1;
+ setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&optval),
+ sizeof optval);
+}
+
+bool TCPStream::setBlocking(bool enabled) {
+ if (m_sd < 0) return true; // silently accept
+#ifdef _WIN32
+ u_long mode = enabled ? 0 : 1;
+ if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) return false;
+#else
+ int flags = fcntl(m_sd, F_GETFL, nullptr);
+ if (flags < 0) return false;
+ if (enabled)
+ flags &= ~O_NONBLOCK;
+ else
+ flags |= O_NONBLOCK;
+ if (fcntl(m_sd, F_SETFL, flags) < 0) return false;
+#endif
+ return true;
+}
+
+int TCPStream::getNativeHandle() const { return m_sd; }
+
+bool TCPStream::WaitForReadEvent(int timeout) {
+ fd_set sdset;
+ struct timeval tv;
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ FD_ZERO(&sdset);
+ FD_SET(m_sd, &sdset);
+ if (select(m_sd + 1, &sdset, NULL, NULL, &tv) > 0) {
+ return true;
+ }
+ return false;
+}