Rename our allwpilib (which is now 2020) to not have 2019 in the name
Change-Id: I3c07f85ed32ab8b97db765a9b43f2a6ce7da964a
diff --git a/wpiutil/src/main/native/cpp/Base64.cpp b/wpiutil/src/main/native/cpp/Base64.cpp
new file mode 100644
index 0000000..35ac76c
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/Base64.cpp
@@ -0,0 +1,170 @@
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#include "wpi/Base64.h"
+
+#include "wpi/SmallVector.h"
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+
+// aaaack but it's fast and const should make it shared text page.
+static const unsigned char pr2six[256] = {
+ // ASCII table
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64,
+ 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64};
+
+size_t Base64Decode(raw_ostream& os, StringRef encoded) {
+ const unsigned char* end = encoded.bytes_begin();
+ while (pr2six[*end] <= 63 && end != encoded.bytes_end()) ++end;
+ size_t nprbytes = end - encoded.bytes_begin();
+ if (nprbytes == 0) return 0;
+
+ const unsigned char* cur = encoded.bytes_begin();
+
+ while (nprbytes > 4) {
+ os << static_cast<unsigned char>(pr2six[cur[0]] << 2 | pr2six[cur[1]] >> 4);
+ os << static_cast<unsigned char>(pr2six[cur[1]] << 4 | pr2six[cur[2]] >> 2);
+ os << static_cast<unsigned char>(pr2six[cur[2]] << 6 | pr2six[cur[3]]);
+ cur += 4;
+ nprbytes -= 4;
+ }
+
+ // Note: (nprbytes == 1) would be an error, so just ignore that case
+ if (nprbytes > 1)
+ os << static_cast<unsigned char>(pr2six[cur[0]] << 2 | pr2six[cur[1]] >> 4);
+ if (nprbytes > 2)
+ os << static_cast<unsigned char>(pr2six[cur[1]] << 4 | pr2six[cur[2]] >> 2);
+ if (nprbytes > 3)
+ os << static_cast<unsigned char>(pr2six[cur[2]] << 6 | pr2six[cur[3]]);
+
+ return (end - encoded.bytes_begin()) + ((4 - nprbytes) & 3);
+}
+
+size_t Base64Decode(StringRef encoded, std::string* plain) {
+ plain->resize(0);
+ raw_string_ostream os(*plain);
+ size_t rv = Base64Decode(os, encoded);
+ os.flush();
+ return rv;
+}
+
+StringRef Base64Decode(StringRef encoded, size_t* num_read,
+ SmallVectorImpl<char>& buf) {
+ buf.clear();
+ raw_svector_ostream os(buf);
+ *num_read = Base64Decode(os, encoded);
+ return os.str();
+}
+
+static const char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void Base64Encode(raw_ostream& os, StringRef plain) {
+ if (plain.empty()) return;
+ size_t len = plain.size();
+
+ size_t i;
+ for (i = 0; (i + 2) < len; i += 3) {
+ os << basis_64[(plain[i] >> 2) & 0x3F];
+ os << basis_64[((plain[i] & 0x3) << 4) |
+ (static_cast<int>(plain[i + 1] & 0xF0) >> 4)];
+ os << basis_64[((plain[i + 1] & 0xF) << 2) |
+ (static_cast<int>(plain[i + 2] & 0xC0) >> 6)];
+ os << basis_64[plain[i + 2] & 0x3F];
+ }
+ if (i < len) {
+ os << basis_64[(plain[i] >> 2) & 0x3F];
+ if (i == (len - 1)) {
+ os << basis_64[((plain[i] & 0x3) << 4)];
+ os << '=';
+ } else {
+ os << basis_64[((plain[i] & 0x3) << 4) |
+ (static_cast<int>(plain[i + 1] & 0xF0) >> 4)];
+ os << basis_64[((plain[i + 1] & 0xF) << 2)];
+ }
+ os << '=';
+ }
+}
+
+void Base64Encode(StringRef plain, std::string* encoded) {
+ encoded->resize(0);
+ raw_string_ostream os(*encoded);
+ Base64Encode(os, plain);
+ os.flush();
+}
+
+StringRef Base64Encode(StringRef plain, SmallVectorImpl<char>& buf) {
+ buf.clear();
+ raw_svector_ostream os(buf);
+ Base64Encode(os, plain);
+ return os.str();
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/EventLoopRunner.cpp b/wpiutil/src/main/native/cpp/EventLoopRunner.cpp
new file mode 100644
index 0000000..1c54bdf
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/EventLoopRunner.cpp
@@ -0,0 +1,81 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/EventLoopRunner.h"
+
+#include "wpi/SmallVector.h"
+#include "wpi/condition_variable.h"
+#include "wpi/mutex.h"
+#include "wpi/uv/AsyncFunction.h"
+#include "wpi/uv/Loop.h"
+
+using namespace wpi;
+
+class EventLoopRunner::Thread : public SafeThread {
+ public:
+ using UvExecFunc = uv::AsyncFunction<void(LoopFunc)>;
+
+ Thread() : m_loop(uv::Loop::Create()) {
+ // set up async handles
+ if (!m_loop) return;
+
+ // run function
+ m_doExec = UvExecFunc::Create(
+ m_loop, [loop = m_loop.get()](auto out, LoopFunc func) {
+ func(*loop);
+ out.set_value();
+ });
+ }
+
+ void Main() {
+ if (m_loop) m_loop->Run();
+ }
+
+ // the loop
+ std::shared_ptr<uv::Loop> m_loop;
+
+ // run function
+ std::weak_ptr<UvExecFunc> m_doExec;
+};
+
+EventLoopRunner::EventLoopRunner() { m_owner.Start(); }
+
+EventLoopRunner::~EventLoopRunner() { Stop(); }
+
+void EventLoopRunner::Stop() {
+ ExecAsync([](uv::Loop& loop) {
+ // close all handles; this will (eventually) stop the loop
+ loop.Walk([](uv::Handle& h) {
+ h.SetLoopClosing(true);
+ h.Close();
+ });
+ });
+ m_owner.Join();
+}
+
+void EventLoopRunner::ExecAsync(LoopFunc func) {
+ if (auto thr = m_owner.GetThread()) {
+ if (auto doExec = thr->m_doExec.lock()) {
+ doExec->Call(func);
+ }
+ }
+}
+
+void EventLoopRunner::ExecSync(LoopFunc func) {
+ wpi::future<void> f;
+ if (auto thr = m_owner.GetThread()) {
+ if (auto doExec = thr->m_doExec.lock()) {
+ f = doExec->Call(func);
+ }
+ }
+ if (f.valid()) f.wait();
+}
+
+std::shared_ptr<uv::Loop> EventLoopRunner::GetLoop() {
+ if (auto thr = m_owner.GetThread()) return thr->m_loop;
+ return nullptr;
+}
diff --git a/wpiutil/src/main/native/cpp/HttpParser.cpp b/wpiutil/src/main/native/cpp/HttpParser.cpp
new file mode 100644
index 0000000..4560b38
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/HttpParser.cpp
@@ -0,0 +1,176 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/HttpParser.h"
+
+using namespace wpi;
+
+uint32_t HttpParser::GetParserVersion() {
+ return static_cast<uint32_t>(http_parser_version());
+}
+
+HttpParser::HttpParser(Type type) {
+ http_parser_init(&m_parser,
+ static_cast<http_parser_type>(static_cast<int>(type)));
+ m_parser.data = this;
+
+ http_parser_settings_init(&m_settings);
+
+ // Unlike the underlying http_parser library, we don't perform callbacks
+ // (other than body) with partial data; instead we buffer and call the user
+ // callback only when the data is complete.
+
+ // on_message_begin: initialize our state, call user callback
+ m_settings.on_message_begin = [](http_parser* p) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+ self.m_urlBuf.clear();
+ self.m_state = kStart;
+ self.messageBegin();
+ return self.m_aborted;
+ };
+
+ // on_url: collect into buffer
+ m_settings.on_url = [](http_parser* p, const char* at, size_t length) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+ // append to buffer
+ if ((self.m_urlBuf.size() + length) > self.m_maxLength) return 1;
+ self.m_urlBuf += StringRef{at, length};
+ self.m_state = kUrl;
+ return 0;
+ };
+
+ // on_status: collect into buffer, call user URL callback
+ m_settings.on_status = [](http_parser* p, const char* at,
+ size_t length) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+ // use valueBuf for the status
+ if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
+ self.m_valueBuf += StringRef{at, length};
+ self.m_state = kStatus;
+ return 0;
+ };
+
+ // on_header_field: collect into buffer, call user header/status callback
+ m_settings.on_header_field = [](http_parser* p, const char* at,
+ size_t length) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+
+ // once we're in header, we know the URL is complete
+ if (self.m_state == kUrl) {
+ self.url(self.m_urlBuf);
+ if (self.m_aborted) return 1;
+ }
+
+ // once we're in header, we know the status is complete
+ if (self.m_state == kStatus) {
+ self.status(self.m_valueBuf);
+ if (self.m_aborted) return 1;
+ }
+
+ // if we previously were in value state, that means we finished a header
+ if (self.m_state == kValue) {
+ self.header(self.m_fieldBuf, self.m_valueBuf);
+ if (self.m_aborted) return 1;
+ }
+
+ // clear field and value when we enter this state
+ if (self.m_state != kField) {
+ self.m_state = kField;
+ self.m_fieldBuf.clear();
+ self.m_valueBuf.clear();
+ }
+
+ // append data to field buffer
+ if ((self.m_fieldBuf.size() + length) > self.m_maxLength) return 1;
+ self.m_fieldBuf += StringRef{at, length};
+ return 0;
+ };
+
+ // on_header_field: collect into buffer
+ m_settings.on_header_value = [](http_parser* p, const char* at,
+ size_t length) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+
+ // if we weren't previously in value state, clear the buffer
+ if (self.m_state != kValue) {
+ self.m_state = kValue;
+ self.m_valueBuf.clear();
+ }
+
+ // append data to value buffer
+ if ((self.m_valueBuf.size() + length) > self.m_maxLength) return 1;
+ self.m_valueBuf += StringRef{at, length};
+ return 0;
+ };
+
+ // on_headers_complete: call user status/header/complete callback
+ m_settings.on_headers_complete = [](http_parser* p) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+
+ // if we previously were in url state, that means we finished the url
+ if (self.m_state == kUrl) {
+ self.url(self.m_urlBuf);
+ if (self.m_aborted) return 1;
+ }
+
+ // if we previously were in status state, that means we finished the status
+ if (self.m_state == kStatus) {
+ self.status(self.m_valueBuf);
+ if (self.m_aborted) return 1;
+ }
+
+ // if we previously were in value state, that means we finished a header
+ if (self.m_state == kValue) {
+ self.header(self.m_fieldBuf, self.m_valueBuf);
+ if (self.m_aborted) return 1;
+ }
+
+ self.headersComplete(self.ShouldKeepAlive());
+ return self.m_aborted;
+ };
+
+ // on_body: call user callback
+ m_settings.on_body = [](http_parser* p, const char* at,
+ size_t length) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+ self.body(StringRef{at, length}, self.IsBodyFinal());
+ return self.m_aborted;
+ };
+
+ // on_message_complete: call user callback
+ m_settings.on_message_complete = [](http_parser* p) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+ self.messageComplete(self.ShouldKeepAlive());
+ return self.m_aborted;
+ };
+
+ // on_chunk_header: call user callback
+ m_settings.on_chunk_header = [](http_parser* p) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+ self.chunkHeader(p->content_length);
+ return self.m_aborted;
+ };
+
+ // on_chunk_complete: call user callback
+ m_settings.on_chunk_complete = [](http_parser* p) -> int {
+ auto& self = *static_cast<HttpParser*>(p->data);
+ self.chunkComplete();
+ return self.m_aborted;
+ };
+}
+
+void HttpParser::Reset(Type type) {
+ http_parser_init(&m_parser,
+ static_cast<http_parser_type>(static_cast<int>(type)));
+ m_parser.data = this;
+ m_maxLength = 1024;
+ m_state = kStart;
+ m_urlBuf.clear();
+ m_fieldBuf.clear();
+ m_valueBuf.clear();
+ m_aborted = false;
+}
diff --git a/wpiutil/src/main/native/cpp/HttpServerConnection.cpp b/wpiutil/src/main/native/cpp/HttpServerConnection.cpp
new file mode 100644
index 0000000..0308e8c
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/HttpServerConnection.cpp
@@ -0,0 +1,162 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/HttpServerConnection.h"
+
+#include "wpi/SmallString.h"
+#include "wpi/SmallVector.h"
+#include "wpi/raw_uv_ostream.h"
+
+using namespace wpi;
+
+HttpServerConnection::HttpServerConnection(std::shared_ptr<uv::Stream> stream)
+ : m_stream(*stream) {
+ // process HTTP messages
+ m_messageCompleteConn =
+ m_request.messageComplete.connect_connection([this](bool keepAlive) {
+ m_keepAlive = keepAlive;
+ ProcessRequest();
+ });
+
+ // look for Accept-Encoding headers to determine if gzip is acceptable
+ m_request.messageBegin.connect([this] { m_acceptGzip = false; });
+ m_request.header.connect([this](StringRef name, StringRef value) {
+ if (name.equals_lower("accept-encoding") && value.contains("gzip")) {
+ m_acceptGzip = true;
+ }
+ });
+
+ // pass incoming data to HTTP parser
+ m_dataConn =
+ stream->data.connect_connection([this](uv::Buffer& buf, size_t size) {
+ m_request.Execute(StringRef{buf.base, size});
+ if (m_request.HasError()) {
+ // could not parse; just close the connection
+ m_stream.Close();
+ }
+ });
+
+ // close when remote side closes
+ m_endConn =
+ stream->end.connect_connection([h = stream.get()] { h->Close(); });
+
+ // start reading
+ stream->StartRead();
+}
+
+void HttpServerConnection::BuildCommonHeaders(raw_ostream& os) {
+ os << "Server: WebServer/1.0\r\n"
+ "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
+ "post-check=0, max-age=0\r\n"
+ "Pragma: no-cache\r\n"
+ "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n";
+}
+
+void HttpServerConnection::BuildHeader(raw_ostream& os, int code,
+ const Twine& codeText,
+ const Twine& contentType,
+ uint64_t contentLength,
+ const Twine& extra) {
+ os << "HTTP/" << m_request.GetMajor() << '.' << m_request.GetMinor() << ' '
+ << code << ' ' << codeText << "\r\n";
+ if (contentLength == 0) m_keepAlive = false;
+ if (!m_keepAlive) os << "Connection: close\r\n";
+ BuildCommonHeaders(os);
+ os << "Content-Type: " << contentType << "\r\n";
+ if (contentLength != 0) os << "Content-Length: " << contentLength << "\r\n";
+ os << "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\n";
+ SmallString<128> extraBuf;
+ StringRef extraStr = extra.toStringRef(extraBuf);
+ if (!extraStr.empty()) os << extraStr;
+ os << "\r\n"; // header ends with a blank line
+}
+
+void HttpServerConnection::SendData(ArrayRef<uv::Buffer> bufs,
+ bool closeAfter) {
+ m_stream.Write(bufs, [closeAfter, stream = &m_stream](
+ MutableArrayRef<uv::Buffer> bufs, uv::Error) {
+ for (auto&& buf : bufs) buf.Deallocate();
+ if (closeAfter) stream->Close();
+ });
+}
+
+void HttpServerConnection::SendResponse(int code, const Twine& codeText,
+ const Twine& contentType,
+ StringRef content,
+ const Twine& extraHeader) {
+ SmallVector<uv::Buffer, 4> toSend;
+ raw_uv_ostream os{toSend, 4096};
+ BuildHeader(os, code, codeText, contentType, content.size(), extraHeader);
+ os << content;
+ // close after write completes if we aren't keeping alive
+ SendData(os.bufs(), !m_keepAlive);
+}
+
+void HttpServerConnection::SendStaticResponse(int code, const Twine& codeText,
+ const Twine& contentType,
+ StringRef content, bool gzipped,
+ const Twine& extraHeader) {
+ // TODO: handle remote side not accepting gzip (very rare)
+
+ StringRef contentEncodingHeader;
+ if (gzipped /* && m_acceptGzip*/)
+ contentEncodingHeader = "Content-Encoding: gzip\r\n";
+
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os{bufs, 4096};
+ BuildHeader(os, code, codeText, contentType, content.size(),
+ extraHeader + contentEncodingHeader);
+ // can send content without copying
+ bufs.emplace_back(content);
+
+ m_stream.Write(bufs, [closeAfter = !m_keepAlive, stream = &m_stream](
+ MutableArrayRef<uv::Buffer> bufs, uv::Error) {
+ // don't deallocate the static content
+ for (auto&& buf : bufs.drop_back()) buf.Deallocate();
+ if (closeAfter) stream->Close();
+ });
+}
+
+void HttpServerConnection::SendError(int code, const Twine& message) {
+ StringRef codeText, extra, baseMessage;
+ switch (code) {
+ case 401:
+ codeText = "Unauthorized";
+ extra = "WWW-Authenticate: Basic realm=\"CameraServer\"";
+ baseMessage = "401: Not Authenticated!";
+ break;
+ case 404:
+ codeText = "Not Found";
+ baseMessage = "404: Not Found!";
+ break;
+ case 500:
+ codeText = "Internal Server Error";
+ baseMessage = "500: Internal Server Error!";
+ break;
+ case 400:
+ codeText = "Bad Request";
+ baseMessage = "400: Not Found!";
+ break;
+ case 403:
+ codeText = "Forbidden";
+ baseMessage = "403: Forbidden!";
+ break;
+ case 503:
+ codeText = "Service Unavailable";
+ baseMessage = "503: Service Unavailable";
+ break;
+ default:
+ code = 501;
+ codeText = "Not Implemented";
+ baseMessage = "501: Not Implemented!";
+ break;
+ }
+ SmallString<256> content = baseMessage;
+ content += "\r\n";
+ message.toVector(content);
+ SendResponse(code, codeText, "text/plain", content, extra);
+}
diff --git a/wpiutil/src/main/native/cpp/HttpUtil.cpp b/wpiutil/src/main/native/cpp/HttpUtil.cpp
new file mode 100644
index 0000000..37fea60
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/HttpUtil.cpp
@@ -0,0 +1,410 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/HttpUtil.h"
+
+#include <cctype>
+
+#include "wpi/Base64.h"
+#include "wpi/STLExtras.h"
+#include "wpi/StringExtras.h"
+#include "wpi/TCPConnector.h"
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+
+StringRef UnescapeURI(const Twine& str, SmallVectorImpl<char>& buf,
+ bool* error) {
+ SmallString<128> strBuf;
+ StringRef strStr = str.toStringRef(strBuf);
+ buf.clear();
+ for (auto i = strStr.begin(), end = strStr.end(); i != end; ++i) {
+ // pass non-escaped characters to output
+ if (*i != '%') {
+ // decode + to space
+ if (*i == '+')
+ buf.push_back(' ');
+ else
+ buf.push_back(*i);
+ continue;
+ }
+
+ // are there enough characters left?
+ if (i + 2 >= end) {
+ *error = true;
+ return StringRef{};
+ }
+
+ // replace %xx with the corresponding character
+ unsigned val1 = hexDigitValue(*++i);
+ if (val1 == -1U) {
+ *error = true;
+ return StringRef{};
+ }
+ unsigned val2 = hexDigitValue(*++i);
+ if (val2 == -1U) {
+ *error = true;
+ return StringRef{};
+ }
+ buf.push_back((val1 << 4) | val2);
+ }
+
+ *error = false;
+ return StringRef{buf.data(), buf.size()};
+}
+
+StringRef EscapeURI(const Twine& str, SmallVectorImpl<char>& buf,
+ bool spacePlus) {
+ static const char* const hexLut = "0123456789ABCDEF";
+
+ SmallString<128> strBuf;
+ StringRef strStr = str.toStringRef(strBuf);
+ buf.clear();
+ for (auto i = strStr.begin(), end = strStr.end(); i != end; ++i) {
+ // pass unreserved characters to output
+ if (std::isalnum(*i) || *i == '-' || *i == '_' || *i == '.' || *i == '~') {
+ buf.push_back(*i);
+ continue;
+ }
+
+ // encode space to +
+ if (spacePlus && *i == ' ') {
+ buf.push_back('+');
+ continue;
+ }
+
+ // convert others to %xx
+ buf.push_back('%');
+ buf.push_back(hexLut[((*i) >> 4) & 0x0f]);
+ buf.push_back(hexLut[(*i) & 0x0f]);
+ }
+
+ return StringRef{buf.data(), buf.size()};
+}
+
+bool ParseHttpHeaders(raw_istream& is, SmallVectorImpl<char>* contentType,
+ SmallVectorImpl<char>* contentLength) {
+ if (contentType) contentType->clear();
+ if (contentLength) contentLength->clear();
+
+ bool inContentType = false;
+ bool inContentLength = false;
+ SmallString<64> lineBuf;
+ for (;;) {
+ StringRef line = is.getline(lineBuf, 1024).rtrim();
+ if (is.has_error()) return false;
+ if (line.empty()) return true; // empty line signals end of headers
+
+ // header fields start at the beginning of the line
+ if (!std::isspace(line[0])) {
+ inContentType = false;
+ inContentLength = false;
+ StringRef field;
+ std::tie(field, line) = line.split(':');
+ field = field.rtrim();
+ if (field.equals_lower("content-type"))
+ inContentType = true;
+ else if (field.equals_lower("content-length"))
+ inContentLength = true;
+ else
+ continue; // ignore other fields
+ }
+
+ // collapse whitespace
+ line = line.ltrim();
+
+ // save field data
+ if (inContentType && contentType)
+ contentType->append(line.begin(), line.end());
+ else if (inContentLength && contentLength)
+ contentLength->append(line.begin(), line.end());
+ }
+}
+
+bool FindMultipartBoundary(raw_istream& is, StringRef boundary,
+ std::string* saveBuf) {
+ SmallString<64> searchBuf;
+ searchBuf.resize(boundary.size() + 2);
+ size_t searchPos = 0;
+
+ // Per the spec, the --boundary should be preceded by \r\n, so do a first
+ // pass of 1-byte reads to throw those away (common case) and keep the
+ // last non-\r\n character in searchBuf.
+ if (!saveBuf) {
+ do {
+ is.read(searchBuf.data(), 1);
+ if (is.has_error()) return false;
+ } while (searchBuf[0] == '\r' || searchBuf[0] == '\n');
+ searchPos = 1;
+ }
+
+ // Look for --boundary. Read boundarysize+2 bytes at a time
+ // during the search to speed up the reads, then fast-scan for -,
+ // and only then match the entire boundary. This will be slow if
+ // there's a bunch of continuous -'s in the output, but that's unlikely.
+ for (;;) {
+ is.read(searchBuf.data() + searchPos, searchBuf.size() - searchPos);
+ if (is.has_error()) return false;
+
+ // Did we find the boundary?
+ if (searchBuf[0] == '-' && searchBuf[1] == '-' &&
+ searchBuf.substr(2) == boundary)
+ return true;
+
+ // Fast-scan for '-'
+ size_t pos = searchBuf.find('-', searchBuf[0] == '-' ? 1 : 0);
+ if (pos == StringRef::npos) {
+ if (saveBuf) saveBuf->append(searchBuf.data(), searchBuf.size());
+ } else {
+ if (saveBuf) saveBuf->append(searchBuf.data(), pos);
+
+ // move '-' and following to start of buffer (next read will fill)
+ std::memmove(searchBuf.data(), searchBuf.data() + pos,
+ searchBuf.size() - pos);
+ searchPos = searchBuf.size() - pos;
+ }
+ }
+}
+
+HttpLocation::HttpLocation(const Twine& url_, bool* error,
+ std::string* errorMsg)
+ : url{url_.str()} {
+ // Split apart into components
+ StringRef query{url};
+
+ // scheme:
+ StringRef scheme;
+ std::tie(scheme, query) = query.split(':');
+ if (!scheme.equals_lower("http")) {
+ *errorMsg = "only supports http URLs";
+ *error = true;
+ return;
+ }
+
+ // "//"
+ if (!query.startswith("//")) {
+ *errorMsg = "expected http://...";
+ *error = true;
+ return;
+ }
+ query = query.drop_front(2);
+
+ // user:password@host:port/
+ StringRef authority;
+ std::tie(authority, query) = query.split('/');
+
+ StringRef userpass, hostport;
+ std::tie(userpass, hostport) = authority.split('@');
+ // split leaves the RHS empty if the split char isn't present...
+ if (hostport.empty()) {
+ hostport = userpass;
+ userpass = StringRef{};
+ }
+
+ if (!userpass.empty()) {
+ StringRef rawUser, rawPassword;
+ std::tie(rawUser, rawPassword) = userpass.split(':');
+ SmallString<64> userBuf, passBuf;
+ user = UnescapeURI(rawUser, userBuf, error);
+ if (*error) {
+ raw_string_ostream oss(*errorMsg);
+ oss << "could not unescape user \"" << rawUser << "\"";
+ oss.flush();
+ return;
+ }
+ password = UnescapeURI(rawPassword, passBuf, error);
+ if (*error) {
+ raw_string_ostream oss(*errorMsg);
+ oss << "could not unescape password \"" << rawPassword << "\"";
+ oss.flush();
+ return;
+ }
+ }
+
+ StringRef portStr;
+ std::tie(host, portStr) = hostport.rsplit(':');
+ if (host.empty()) {
+ *errorMsg = "host is empty";
+ *error = true;
+ return;
+ }
+ if (portStr.empty()) {
+ port = 80;
+ } else if (portStr.getAsInteger(10, port)) {
+ raw_string_ostream oss(*errorMsg);
+ oss << "port \"" << portStr << "\" is not an integer";
+ oss.flush();
+ *error = true;
+ return;
+ }
+
+ // path?query#fragment
+ std::tie(query, fragment) = query.split('#');
+ std::tie(path, query) = query.split('?');
+
+ // Split query string into parameters
+ while (!query.empty()) {
+ // split out next param and value
+ StringRef rawParam, rawValue;
+ std::tie(rawParam, query) = query.split('&');
+ if (rawParam.empty()) continue; // ignore "&&"
+ std::tie(rawParam, rawValue) = rawParam.split('=');
+
+ // unescape param
+ *error = false;
+ SmallString<64> paramBuf;
+ StringRef param = UnescapeURI(rawParam, paramBuf, error);
+ if (*error) {
+ raw_string_ostream oss(*errorMsg);
+ oss << "could not unescape parameter \"" << rawParam << "\"";
+ oss.flush();
+ return;
+ }
+
+ // unescape value
+ SmallString<64> valueBuf;
+ StringRef value = UnescapeURI(rawValue, valueBuf, error);
+ if (*error) {
+ raw_string_ostream oss(*errorMsg);
+ oss << "could not unescape value \"" << rawValue << "\"";
+ oss.flush();
+ return;
+ }
+
+ params.emplace_back(std::make_pair(param, value));
+ }
+
+ *error = false;
+}
+
+void HttpRequest::SetAuth(const HttpLocation& loc) {
+ if (!loc.user.empty()) {
+ SmallString<64> userpass;
+ userpass += loc.user;
+ userpass += ':';
+ userpass += loc.password;
+ Base64Encode(userpass, &auth);
+ }
+}
+
+bool HttpConnection::Handshake(const HttpRequest& request,
+ std::string* warnMsg) {
+ // send GET request
+ os << "GET /" << request.path << " HTTP/1.1\r\n";
+ os << "Host: " << request.host << "\r\n";
+ if (!request.auth.empty())
+ os << "Authorization: Basic " << request.auth << "\r\n";
+ os << "\r\n";
+ os.flush();
+
+ // read first line of response
+ SmallString<64> lineBuf;
+ StringRef line = is.getline(lineBuf, 1024).rtrim();
+ if (is.has_error()) {
+ *warnMsg = "disconnected before response";
+ return false;
+ }
+
+ // see if we got a HTTP 200 response
+ StringRef httpver, code, codeText;
+ std::tie(httpver, line) = line.split(' ');
+ std::tie(code, codeText) = line.split(' ');
+ if (!httpver.startswith("HTTP")) {
+ *warnMsg = "did not receive HTTP response";
+ return false;
+ }
+ if (code != "200") {
+ raw_string_ostream oss(*warnMsg);
+ oss << "received " << code << " " << codeText << " response";
+ oss.flush();
+ return false;
+ }
+
+ // Parse headers
+ if (!ParseHttpHeaders(is, &contentType, &contentLength)) {
+ *warnMsg = "disconnected during headers";
+ return false;
+ }
+
+ return true;
+}
+
+void HttpMultipartScanner::SetBoundary(StringRef boundary) {
+ m_boundaryWith = "\n--";
+ m_boundaryWith += boundary;
+ m_boundaryWithout = "\n";
+ m_boundaryWithout += boundary;
+ m_dashes = kUnknown;
+}
+
+void HttpMultipartScanner::Reset(bool saveSkipped) {
+ m_saveSkipped = saveSkipped;
+ m_state = kBoundary;
+ m_posWith = 0;
+ m_posWithout = 0;
+ m_buf.resize(0);
+}
+
+StringRef HttpMultipartScanner::Execute(StringRef in) {
+ if (m_state == kDone) Reset(m_saveSkipped);
+ if (m_saveSkipped) m_buf += in;
+
+ size_t pos = 0;
+ if (m_state == kBoundary) {
+ for (char ch : in) {
+ ++pos;
+ if (m_dashes != kWithout) {
+ if (ch == m_boundaryWith[m_posWith]) {
+ ++m_posWith;
+ if (m_posWith == m_boundaryWith.size()) {
+ // Found the boundary; transition to padding
+ m_state = kPadding;
+ m_dashes = kWith; // no longer accept plain 'boundary'
+ break;
+ }
+ } else if (ch == m_boundaryWith[0]) {
+ m_posWith = 1;
+ } else {
+ m_posWith = 0;
+ }
+ }
+
+ if (m_dashes != kWith) {
+ if (ch == m_boundaryWithout[m_posWithout]) {
+ ++m_posWithout;
+ if (m_posWithout == m_boundaryWithout.size()) {
+ // Found the boundary; transition to padding
+ m_state = kPadding;
+ m_dashes = kWithout; // no longer accept '--boundary'
+ break;
+ }
+ } else if (ch == m_boundaryWithout[0]) {
+ m_posWithout = 1;
+ } else {
+ m_posWithout = 0;
+ }
+ }
+ }
+ }
+
+ if (m_state == kPadding) {
+ for (char ch : in.drop_front(pos)) {
+ ++pos;
+ if (ch == '\n') {
+ // Found the LF; return remaining input buffer (following it)
+ m_state = kDone;
+ if (m_saveSkipped) m_buf.resize(m_buf.size() - in.size() + pos);
+ return in.drop_front(pos);
+ }
+ }
+ }
+
+ // We consumed the entire input
+ return StringRef{};
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/PortForwarder.cpp b/wpiutil/src/main/native/cpp/PortForwarder.cpp
new file mode 100644
index 0000000..fe54b63
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/PortForwarder.cpp
@@ -0,0 +1,150 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/PortForwarder.h"
+
+#include "wpi/DenseMap.h"
+#include "wpi/EventLoopRunner.h"
+#include "wpi/SmallString.h"
+#include "wpi/raw_ostream.h"
+#include "wpi/uv/GetAddrInfo.h"
+#include "wpi/uv/Tcp.h"
+#include "wpi/uv/Timer.h"
+
+using namespace wpi;
+
+struct PortForwarder::Impl {
+ public:
+ EventLoopRunner runner;
+ DenseMap<unsigned int, std::weak_ptr<uv::Tcp>> servers;
+};
+
+PortForwarder::PortForwarder() : m_impl{new Impl} {}
+
+PortForwarder& PortForwarder::GetInstance() {
+ static PortForwarder instance;
+ return instance;
+}
+
+static void CopyStream(uv::Stream& in, std::weak_ptr<uv::Stream> outWeak) {
+ in.data.connect([&in, outWeak](uv::Buffer& buf, size_t len) {
+ uv::Buffer buf2 = buf.Dup();
+ buf2.len = len;
+ auto out = outWeak.lock();
+ if (!out) {
+ in.Close();
+ return;
+ }
+ out->Write(buf2, [](auto bufs, uv::Error) {
+ for (auto buf : bufs) buf.Deallocate();
+ });
+ });
+}
+
+void PortForwarder::Add(unsigned int port, const Twine& remoteHost,
+ unsigned int remotePort) {
+ m_impl->runner.ExecSync([&](uv::Loop& loop) {
+ auto server = uv::Tcp::Create(loop);
+
+ // bind to local port
+ server->Bind("", port);
+
+ // when we get a connection, accept it
+ server->connection.connect([serverPtr = server.get(),
+ host = remoteHost.str(), remotePort] {
+ auto& loop = serverPtr->GetLoopRef();
+ auto client = serverPtr->Accept();
+ if (!client) return;
+
+ // close on error
+ client->error.connect(
+ [clientPtr = client.get()](uv::Error err) { clientPtr->Close(); });
+
+ // connected flag
+ auto connected = std::make_shared<bool>(false);
+ client->SetData(connected);
+
+ auto remote = uv::Tcp::Create(loop);
+ remote->error.connect(
+ [remotePtr = remote.get(),
+ clientWeak = std::weak_ptr<uv::Tcp>(client)](uv::Error err) {
+ remotePtr->Close();
+ if (auto client = clientWeak.lock()) client->Close();
+ });
+
+ // convert port to string
+ SmallString<16> remotePortStr;
+ raw_svector_ostream(remotePortStr) << remotePort;
+
+ // resolve address
+ uv::GetAddrInfo(
+ loop,
+ [clientWeak = std::weak_ptr<uv::Tcp>(client),
+ remoteWeak = std::weak_ptr<uv::Tcp>(remote)](const addrinfo& addr) {
+ auto remote = remoteWeak.lock();
+ if (!remote) return;
+
+ // connect to remote address/port
+ remote->Connect(*addr.ai_addr, [remotePtr = remote.get(),
+ remoteWeak, clientWeak] {
+ auto client = clientWeak.lock();
+ if (!client) {
+ remotePtr->Close();
+ return;
+ }
+ *(client->GetData<bool>()) = true;
+
+ // close both when either side closes
+ client->end.connect([clientPtr = client.get(), remoteWeak] {
+ clientPtr->Close();
+ if (auto remote = remoteWeak.lock()) remote->Close();
+ });
+ remotePtr->end.connect([remotePtr, clientWeak] {
+ remotePtr->Close();
+ if (auto client = clientWeak.lock()) client->Close();
+ });
+
+ // copy bidirectionally
+ client->StartRead();
+ remotePtr->StartRead();
+ CopyStream(*client, remoteWeak);
+ CopyStream(*remotePtr, clientWeak);
+ });
+ },
+ host, remotePortStr);
+
+ // time out for connection
+ uv::Timer::SingleShot(loop, uv::Timer::Time{500},
+ [connectedWeak = std::weak_ptr<bool>(connected),
+ clientWeak = std::weak_ptr<uv::Tcp>(client),
+ remoteWeak = std::weak_ptr<uv::Tcp>(remote)] {
+ if (auto connected = connectedWeak.lock()) {
+ if (!*connected) {
+ if (auto client = clientWeak.lock())
+ client->Close();
+ if (auto remote = remoteWeak.lock())
+ remote->Close();
+ }
+ }
+ });
+ });
+
+ // start listening for incoming connections
+ server->Listen();
+
+ m_impl->servers[port] = server;
+ });
+}
+
+void PortForwarder::Remove(unsigned int port) {
+ m_impl->runner.ExecSync([&](uv::Loop& loop) {
+ if (auto server = m_impl->servers.lookup(port).lock()) {
+ server->Close();
+ m_impl->servers.erase(port);
+ }
+ });
+}
diff --git a/wpiutil/src/main/native/cpp/SafeThread.cpp b/wpiutil/src/main/native/cpp/SafeThread.cpp
new file mode 100644
index 0000000..3a906bd
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/SafeThread.cpp
@@ -0,0 +1,86 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/SafeThread.h"
+
+using namespace wpi;
+
+detail::SafeThreadProxyBase::SafeThreadProxyBase(
+ std::shared_ptr<SafeThread> thr)
+ : m_thread(std::move(thr)) {
+ if (!m_thread) return;
+ m_lock = std::unique_lock<wpi::mutex>(m_thread->m_mutex);
+ if (!m_thread->m_active) {
+ m_lock.unlock();
+ m_thread = nullptr;
+ return;
+ }
+}
+
+detail::SafeThreadOwnerBase::~SafeThreadOwnerBase() {
+ if (m_joinAtExit)
+ Join();
+ else
+ Stop();
+}
+
+void detail::SafeThreadOwnerBase::Start(std::shared_ptr<SafeThread> thr) {
+ std::scoped_lock lock(m_mutex);
+ if (auto thr = m_thread.lock()) return;
+ m_stdThread = std::thread([=] { thr->Main(); });
+ thr->m_threadId = m_stdThread.get_id();
+ m_thread = thr;
+}
+
+void detail::SafeThreadOwnerBase::Stop() {
+ std::scoped_lock lock(m_mutex);
+ if (auto thr = m_thread.lock()) {
+ thr->m_active = false;
+ thr->m_cond.notify_all();
+ m_thread.reset();
+ }
+ if (m_stdThread.joinable()) m_stdThread.detach();
+}
+
+void detail::SafeThreadOwnerBase::Join() {
+ std::unique_lock lock(m_mutex);
+ if (auto thr = m_thread.lock()) {
+ auto stdThread = std::move(m_stdThread);
+ m_thread.reset();
+ lock.unlock();
+ thr->m_active = false;
+ thr->m_cond.notify_all();
+ stdThread.join();
+ } else if (m_stdThread.joinable()) {
+ m_stdThread.detach();
+ }
+}
+
+void detail::swap(SafeThreadOwnerBase& lhs, SafeThreadOwnerBase& rhs) noexcept {
+ using std::swap;
+ if (&lhs == &rhs) return;
+ std::scoped_lock lock(lhs.m_mutex, rhs.m_mutex);
+ std::swap(lhs.m_stdThread, rhs.m_stdThread);
+ std::swap(lhs.m_thread, rhs.m_thread);
+}
+
+detail::SafeThreadOwnerBase::operator bool() const {
+ std::scoped_lock lock(m_mutex);
+ return !m_thread.expired();
+}
+
+std::thread::native_handle_type
+detail::SafeThreadOwnerBase::GetNativeThreadHandle() {
+ std::scoped_lock lock(m_mutex);
+ return m_stdThread.native_handle();
+}
+
+std::shared_ptr<SafeThread> detail::SafeThreadOwnerBase::GetThreadSharedPtr()
+ const {
+ std::scoped_lock lock(m_mutex);
+ return m_thread.lock();
+}
diff --git a/wpiutil/src/main/native/cpp/SocketError.cpp b/wpiutil/src/main/native/cpp/SocketError.cpp
new file mode 100644
index 0000000..1695846
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/SocketError.cpp
@@ -0,0 +1,40 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/SocketError.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <cerrno>
+#include <cstring>
+#endif
+
+namespace wpi {
+
+int SocketErrno() {
+#ifdef _WIN32
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+std::string SocketStrerror(int code) {
+#ifdef _WIN32
+ LPSTR errstr = nullptr;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0,
+ code, 0, (LPSTR)&errstr, 0, 0);
+ std::string rv(errstr);
+ LocalFree(errstr);
+ return rv;
+#else
+ return std::strerror(code);
+#endif
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/TCPAcceptor.cpp b/wpiutil/src/main/native/cpp/TCPAcceptor.cpp
new file mode 100644
index 0000000..90d9496
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/TCPAcceptor.cpp
@@ -0,0 +1,204 @@
+/*
+ TCPAcceptor.cpp
+
+ TCPAcceptor class definition. TCPAcceptor provides methods to passively
+ establish TCP/IP connections with clients.
+
+ ------------------------------------------
+
+ 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/TCPAcceptor.h"
+
+#include <cstdio>
+#include <cstring>
+
+#ifdef _WIN32
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#include <WinSock2.h>
+#include <Ws2tcpip.h>
+#pragma comment(lib, "Ws2_32.lib")
+#else
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+
+#include "wpi/Logger.h"
+#include "wpi/SmallString.h"
+#include "wpi/SocketError.h"
+
+using namespace wpi;
+
+TCPAcceptor::TCPAcceptor(int port, const char* address, Logger& logger)
+ : m_lsd(0),
+ m_port(port),
+ m_address(address),
+ m_listening(false),
+ m_logger(logger) {
+ m_shutdown = false;
+#ifdef _WIN32
+ WSAData wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ (void)WSAStartup(wVersionRequested, &wsaData);
+#endif
+}
+
+TCPAcceptor::~TCPAcceptor() {
+ if (m_lsd > 0) {
+ shutdown();
+#ifdef _WIN32
+ closesocket(m_lsd);
+#else
+ close(m_lsd);
+#endif
+ }
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+int TCPAcceptor::start() {
+ if (m_listening) return 0;
+
+ m_lsd = socket(PF_INET, SOCK_STREAM, 0);
+ if (m_lsd < 0) {
+ WPI_ERROR(m_logger, "could not create socket");
+ return -1;
+ }
+ struct sockaddr_in address;
+
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = PF_INET;
+ if (m_address.size() > 0) {
+#ifdef _WIN32
+ SmallString<128> addr_copy(m_address);
+ addr_copy.push_back('\0');
+ int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
+#else
+ int res = inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
+#endif
+ if (res != 1) {
+ WPI_ERROR(m_logger, "could not resolve " << m_address << " address");
+ return -1;
+ }
+ } else {
+ address.sin_addr.s_addr = INADDR_ANY;
+ }
+ address.sin_port = htons(m_port);
+
+#ifdef _WIN32
+ int optval = 1;
+ setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ reinterpret_cast<char*>(&optval), sizeof optval);
+#else
+ int optval = 1;
+ setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&optval),
+ sizeof optval);
+#endif
+
+ int result = bind(m_lsd, reinterpret_cast<struct sockaddr*>(&address),
+ sizeof(address));
+ if (result != 0) {
+ WPI_ERROR(m_logger,
+ "bind() to port " << m_port << " failed: " << SocketStrerror());
+ return result;
+ }
+
+ result = listen(m_lsd, 5);
+ if (result != 0) {
+ WPI_ERROR(m_logger,
+ "listen() on port " << m_port << " failed: " << SocketStrerror());
+ return result;
+ }
+ m_listening = true;
+ return result;
+}
+
+void TCPAcceptor::shutdown() {
+ m_shutdown = true;
+#ifdef _WIN32
+ ::shutdown(m_lsd, SD_BOTH);
+
+ // this is ugly, but the easiest way to do this
+ // force wakeup of accept() with a non-blocking connect to ourselves
+ struct sockaddr_in address;
+
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = PF_INET;
+ SmallString<128> addr_copy;
+ if (m_address.size() > 0)
+ addr_copy = m_address;
+ else
+ addr_copy = "127.0.0.1";
+ addr_copy.push_back('\0');
+ int size = sizeof(address);
+ if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr,
+ (struct sockaddr*)&address, &size) != 0)
+ return;
+ address.sin_port = htons(m_port);
+
+ int result = -1, sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sd < 0) return;
+
+ // Set socket to non-blocking
+ u_long mode = 1;
+ ioctlsocket(sd, FIONBIO, &mode);
+
+ // Try to connect
+ ::connect(sd, (struct sockaddr*)&address, sizeof(address));
+
+ // Close
+ ::closesocket(sd);
+
+#else
+ ::shutdown(m_lsd, SHUT_RDWR);
+ int nullfd = ::open("/dev/null", O_RDONLY);
+ if (nullfd >= 0) {
+ ::dup2(nullfd, m_lsd);
+ ::close(nullfd);
+ }
+#endif
+}
+
+std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
+ if (!m_listening || m_shutdown) return nullptr;
+
+ struct sockaddr_in address;
+#ifdef _WIN32
+ int len = sizeof(address);
+#else
+ socklen_t len = sizeof(address);
+#endif
+ std::memset(&address, 0, sizeof(address));
+ int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len);
+ if (sd < 0) {
+ if (!m_shutdown)
+ WPI_ERROR(m_logger, "accept() on port "
+ << m_port << " failed: " << SocketStrerror());
+ return nullptr;
+ }
+ if (m_shutdown) {
+#ifdef _WIN32
+ closesocket(sd);
+#else
+ close(sd);
+#endif
+ return nullptr;
+ }
+ return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+}
diff --git a/wpiutil/src/main/native/cpp/TCPConnector.cpp b/wpiutil/src/main/native/cpp/TCPConnector.cpp
new file mode 100644
index 0000000..6110133
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/TCPConnector.cpp
@@ -0,0 +1,218 @@
+/*
+ TCPConnector.h
+
+ TCPConnector class definition. TCPConnector provides methods to actively
+ establish TCP/IP connections with a server.
+
+ ------------------------------------------
+
+ 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/TCPConnector.h"
+
+#include <fcntl.h>
+
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+
+#ifdef _WIN32
+#include <WS2tcpip.h>
+#include <WinSock2.h>
+#else
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <unistd.h>
+#endif
+
+#include "wpi/Logger.h"
+#include "wpi/SmallString.h"
+#include "wpi/SocketError.h"
+#include "wpi/TCPStream.h"
+
+using namespace wpi;
+
+static int ResolveHostName(const char* hostname, struct in_addr* addr) {
+ struct addrinfo hints;
+ struct addrinfo* res;
+
+ hints.ai_flags = 0;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ hints.ai_addrlen = 0;
+ hints.ai_addr = nullptr;
+ hints.ai_canonname = nullptr;
+ hints.ai_next = nullptr;
+ int result = getaddrinfo(hostname, nullptr, &hints, &res);
+ if (result == 0) {
+ std::memcpy(
+ addr, &(reinterpret_cast<struct sockaddr_in*>(res->ai_addr)->sin_addr),
+ sizeof(struct in_addr));
+ freeaddrinfo(res);
+ }
+ return result;
+}
+
+std::unique_ptr<NetworkStream> TCPConnector::connect(const char* server,
+ int port, Logger& logger,
+ int timeout) {
+#ifdef _WIN32
+ struct WSAHelper {
+ WSAHelper() {
+ WSAData wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSAStartup(wVersionRequested, &wsaData);
+ }
+ ~WSAHelper() { WSACleanup(); }
+ };
+ static WSAHelper helper;
+#endif
+ struct sockaddr_in address;
+
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = AF_INET;
+ if (ResolveHostName(server, &(address.sin_addr)) != 0) {
+#ifdef _WIN32
+ SmallString<128> addr_copy(server);
+ addr_copy.push_back('\0');
+ int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
+#else
+ int res = inet_pton(PF_INET, server, &(address.sin_addr));
+#endif
+ if (res != 1) {
+ WPI_ERROR(logger, "could not resolve " << server << " address");
+ return nullptr;
+ }
+ }
+ address.sin_port = htons(port);
+
+ if (timeout == 0) {
+ int sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sd < 0) {
+ WPI_ERROR(logger, "could not create socket");
+ return nullptr;
+ }
+ if (::connect(sd, (struct sockaddr*)&address, sizeof(address)) != 0) {
+ WPI_ERROR(logger, "connect() to " << server << " port " << port
+ << " failed: " << SocketStrerror());
+#ifdef _WIN32
+ closesocket(sd);
+#else
+ ::close(sd);
+#endif
+ return nullptr;
+ }
+ return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+ }
+
+ fd_set sdset;
+ struct timeval tv;
+ socklen_t len;
+ int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sd < 0) {
+ WPI_ERROR(logger, "could not create socket");
+ return nullptr;
+ }
+
+// Set socket to non-blocking
+#ifdef _WIN32
+ u_long mode = 1;
+ if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
+ WPI_WARNING(logger,
+ "could not set socket to non-blocking: " << SocketStrerror());
+#else
+ int arg;
+ arg = fcntl(sd, F_GETFL, nullptr);
+ if (arg < 0) {
+ WPI_WARNING(logger,
+ "could not set socket to non-blocking: " << SocketStrerror());
+ } else {
+ arg |= O_NONBLOCK;
+ if (fcntl(sd, F_SETFL, arg) < 0)
+ WPI_WARNING(logger,
+ "could not set socket to non-blocking: " << SocketStrerror());
+ }
+#endif
+
+ // Connect with time limit
+ if ((result = ::connect(sd, (struct sockaddr*)&address, sizeof(address))) <
+ 0) {
+ int my_errno = SocketErrno();
+#ifdef _WIN32
+ if (my_errno == WSAEWOULDBLOCK || my_errno == WSAEINPROGRESS) {
+#else
+ if (my_errno == EWOULDBLOCK || my_errno == EINPROGRESS) {
+#endif
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ FD_ZERO(&sdset);
+ FD_SET(sd, &sdset);
+ if (select(sd + 1, nullptr, &sdset, nullptr, &tv) > 0) {
+ len = sizeof(int);
+ getsockopt(sd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&valopt),
+ &len);
+ if (valopt) {
+ WPI_ERROR(logger, "select() to " << server << " port " << port
+ << " error " << valopt << " - "
+ << SocketStrerror(valopt));
+ }
+ // connection established
+ else
+ result = 0;
+ } else {
+ WPI_INFO(logger,
+ "connect() to " << server << " port " << port << " timed out");
+ }
+ } else {
+ WPI_ERROR(logger, "connect() to " << server << " port " << port
+ << " error " << SocketErrno() << " - "
+ << SocketStrerror());
+ }
+ }
+
+// Return socket to blocking mode
+#ifdef _WIN32
+ mode = 0;
+ if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
+ WPI_WARNING(logger,
+ "could not set socket to blocking: " << SocketStrerror());
+#else
+ arg = fcntl(sd, F_GETFL, nullptr);
+ if (arg < 0) {
+ WPI_WARNING(logger,
+ "could not set socket to blocking: " << SocketStrerror());
+ } else {
+ arg &= (~O_NONBLOCK);
+ if (fcntl(sd, F_SETFL, arg) < 0)
+ WPI_WARNING(logger,
+ "could not set socket to blocking: " << SocketStrerror());
+ }
+#endif
+
+ // Create stream object if connected, close if not.
+ if (result == -1) {
+#ifdef _WIN32
+ closesocket(sd);
+#else
+ ::close(sd);
+#endif
+ return nullptr;
+ }
+ return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+}
diff --git a/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp b/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp
new file mode 100644
index 0000000..de8bc2c
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/TCPConnector_parallel.cpp
@@ -0,0 +1,129 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/TCPConnector.h" // NOLINT(build/include_order)
+
+#include <atomic>
+#include <chrono>
+#include <thread>
+#include <tuple>
+
+#include "wpi/SmallSet.h"
+#include "wpi/condition_variable.h"
+#include "wpi/mutex.h"
+
+using namespace wpi;
+
+// MSVC < 1900 doesn't have support for thread_local
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+// clang check for availability of thread_local
+#if !defined(__has_feature) || __has_feature(cxx_thread_local)
+#define HAVE_THREAD_LOCAL
+#endif
+#endif
+
+std::unique_ptr<NetworkStream> TCPConnector::connect_parallel(
+ ArrayRef<std::pair<const char*, int>> servers, Logger& logger,
+ int timeout) {
+ if (servers.empty()) return nullptr;
+
+ // structure to make sure we don't start duplicate workers
+ struct GlobalState {
+ wpi::mutex mtx;
+#ifdef HAVE_THREAD_LOCAL
+ SmallSet<std::pair<std::string, int>, 16> active;
+#else
+ SmallSet<std::tuple<std::thread::id, std::string, int>, 16> active;
+#endif
+ };
+#ifdef HAVE_THREAD_LOCAL
+ thread_local auto global = std::make_shared<GlobalState>();
+#else
+ static auto global = std::make_shared<GlobalState>();
+ auto this_id = std::this_thread::get_id();
+#endif
+ auto local = global; // copy to an automatic variable for lambda capture
+
+ // structure shared between threads and this function
+ struct Result {
+ wpi::mutex mtx;
+ wpi::condition_variable cv;
+ std::unique_ptr<NetworkStream> stream;
+ std::atomic<unsigned int> count{0};
+ std::atomic<bool> done{false};
+ };
+ auto result = std::make_shared<Result>();
+
+ // start worker threads; this is I/O bound so we don't limit to # of procs
+ Logger* plogger = &logger;
+ unsigned int num_workers = 0;
+ for (const auto& server : servers) {
+ std::pair<std::string, int> server_copy{std::string{server.first},
+ server.second};
+#ifdef HAVE_THREAD_LOCAL
+ const auto& active_tracker = server_copy;
+#else
+ std::tuple<std::thread::id, std::string, int> active_tracker{
+ this_id, server_copy.first, server_copy.second};
+#endif
+
+ // don't start a new worker if we had a previously still-active connection
+ // attempt to the same server
+ {
+ std::scoped_lock lock(local->mtx);
+ if (local->active.count(active_tracker) > 0) continue; // already in set
+ }
+
+ ++num_workers;
+
+ // start the worker
+ std::thread([=]() {
+ if (!result->done) {
+ // add to global state
+ {
+ std::scoped_lock lock(local->mtx);
+ local->active.insert(active_tracker);
+ }
+
+ // try to connect
+ auto stream = connect(server_copy.first.c_str(), server_copy.second,
+ *plogger, timeout);
+
+ // remove from global state
+ {
+ std::scoped_lock lock(local->mtx);
+ local->active.erase(active_tracker);
+ }
+
+ // successful connection
+ if (stream) {
+ std::scoped_lock lock(result->mtx);
+ if (!result->done.exchange(true)) result->stream = std::move(stream);
+ }
+ }
+ ++result->count;
+ result->cv.notify_all();
+ })
+ .detach();
+ }
+
+ // wait for a result, timeout, or all finished
+ std::unique_lock lock(result->mtx);
+ if (timeout == 0) {
+ result->cv.wait(
+ lock, [&] { return result->stream || result->count >= num_workers; });
+ } else {
+ auto timeout_time =
+ std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
+ result->cv.wait_until(lock, timeout_time, [&] {
+ return result->stream || result->count >= num_workers;
+ });
+ }
+
+ // no need to wait for remaining worker threads; shared_ptr will clean up
+ return std::move(result->stream);
+}
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;
+}
diff --git a/wpiutil/src/main/native/cpp/UDPClient.cpp b/wpiutil/src/main/native/cpp/UDPClient.cpp
new file mode 100644
index 0000000..aafdf47
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/UDPClient.cpp
@@ -0,0 +1,237 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/UDPClient.h"
+
+#ifdef _WIN32
+#include <WinSock2.h>
+#include <Ws2tcpip.h>
+#pragma comment(lib, "Ws2_32.lib")
+#else
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+
+#include "wpi/Logger.h"
+#include "wpi/SocketError.h"
+
+using namespace wpi;
+
+UDPClient::UDPClient(Logger& logger) : UDPClient("", logger) {}
+
+UDPClient::UDPClient(const Twine& address, Logger& logger)
+ : m_lsd(0), m_port(0), m_address(address.str()), m_logger(logger) {}
+
+UDPClient::UDPClient(UDPClient&& other)
+ : m_lsd(other.m_lsd),
+ m_port(other.m_port),
+ m_address(std::move(other.m_address)),
+ m_logger(other.m_logger) {
+ other.m_lsd = 0;
+ other.m_port = 0;
+}
+
+UDPClient::~UDPClient() {
+ if (m_lsd > 0) {
+ shutdown();
+ }
+}
+
+UDPClient& UDPClient::operator=(UDPClient&& other) {
+ if (this == &other) return *this;
+ shutdown();
+ m_logger = other.m_logger;
+ m_lsd = other.m_lsd;
+ m_address = std::move(other.m_address);
+ m_port = other.m_port;
+ other.m_lsd = 0;
+ other.m_port = 0;
+ return *this;
+}
+
+int UDPClient::start() { return start(0); }
+
+int UDPClient::start(int port) {
+ if (m_lsd > 0) return 0;
+
+#ifdef _WIN32
+ WSAData wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSAStartup(wVersionRequested, &wsaData);
+#endif
+
+ m_lsd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (m_lsd < 0) {
+ WPI_ERROR(m_logger, "could not create socket");
+ return -1;
+ }
+
+ struct sockaddr_in addr;
+ std::memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ if (m_address.size() > 0) {
+#ifdef _WIN32
+ SmallString<128> addr_copy(m_address);
+ addr_copy.push_back('\0');
+ int res = InetPton(PF_INET, addr_copy.data(), &(addr.sin_addr));
+#else
+ int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr));
+#endif
+ if (res != 1) {
+ WPI_ERROR(m_logger, "could not resolve " << m_address << " address");
+ return -1;
+ }
+ } else {
+ addr.sin_addr.s_addr = INADDR_ANY;
+ }
+ addr.sin_port = htons(port);
+
+ if (port != 0) {
+#ifdef _WIN32
+ int optval = 1;
+ setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ reinterpret_cast<char*>(&optval), sizeof optval);
+#else
+ int optval = 1;
+ setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR,
+ reinterpret_cast<char*>(&optval), sizeof optval);
+#endif
+ }
+
+ int result = bind(m_lsd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (result != 0) {
+ WPI_ERROR(m_logger, "bind() failed: " << SocketStrerror());
+ return result;
+ }
+ m_port = port;
+ return 0;
+}
+
+void UDPClient::shutdown() {
+ if (m_lsd > 0) {
+#ifdef _WIN32
+ ::shutdown(m_lsd, SD_BOTH);
+ closesocket(m_lsd);
+ WSACleanup();
+#else
+ ::shutdown(m_lsd, SHUT_RDWR);
+ close(m_lsd);
+#endif
+ m_lsd = 0;
+ m_port = 0;
+ }
+}
+
+int UDPClient::send(ArrayRef<uint8_t> data, const Twine& server, int port) {
+ // server must be a resolvable IP address
+ struct sockaddr_in addr;
+ std::memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ SmallVector<char, 128> addr_store;
+ StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store);
+ if (remoteAddr.empty()) {
+ WPI_ERROR(m_logger, "server must be passed");
+ return -1;
+ }
+
+#ifdef _WIN32
+ int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+#else
+ int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+#endif
+ if (res != 1) {
+ WPI_ERROR(m_logger, "could not resolve " << server << " address");
+ return -1;
+ }
+ addr.sin_port = htons(port);
+
+ // sendto should not block
+ int result =
+ sendto(m_lsd, reinterpret_cast<const char*>(data.data()), data.size(), 0,
+ reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ return result;
+}
+
+int UDPClient::send(StringRef data, const Twine& server, int port) {
+ // server must be a resolvable IP address
+ struct sockaddr_in addr;
+ std::memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ SmallVector<char, 128> addr_store;
+ StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store);
+ if (remoteAddr.empty()) {
+ WPI_ERROR(m_logger, "server must be passed");
+ return -1;
+ }
+
+#ifdef _WIN32
+ int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+#else
+ int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
+#endif
+ if (res != 1) {
+ WPI_ERROR(m_logger, "could not resolve " << server << " address");
+ return -1;
+ }
+ addr.sin_port = htons(port);
+
+ // sendto should not block
+ int result = sendto(m_lsd, data.data(), data.size(), 0,
+ reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ return result;
+}
+
+int UDPClient::receive(uint8_t* data_received, int receive_len) {
+ if (m_port == 0) return -1; // return if not receiving
+ return recv(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0);
+}
+
+int UDPClient::receive(uint8_t* data_received, int receive_len,
+ SmallVectorImpl<char>* addr_received,
+ int* port_received) {
+ if (m_port == 0) return -1; // return if not receiving
+
+ struct sockaddr_in remote;
+ socklen_t remote_len = sizeof(remote);
+ std::memset(&remote, 0, sizeof(remote));
+
+ int result =
+ recvfrom(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0,
+ reinterpret_cast<sockaddr*>(&remote), &remote_len);
+
+ char ip[50];
+#ifdef _WIN32
+ InetNtop(PF_INET, &(remote.sin_addr.s_addr), ip, sizeof(ip) - 1);
+#else
+ inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(remote.sin_addr.s_addr)), ip,
+ sizeof(ip) - 1);
+#endif
+
+ ip[49] = '\0';
+ int addr_len = std::strlen(ip);
+ addr_received->clear();
+ addr_received->append(&ip[0], &ip[addr_len]);
+
+ *port_received = ntohs(remote.sin_port);
+
+ return result;
+}
+
+int UDPClient::set_timeout(double timeout) {
+ if (timeout < 0) return -1;
+ struct timeval tv;
+ tv.tv_sec = timeout; // truncating will give seconds
+ timeout -= tv.tv_sec; // remove seconds portion
+ tv.tv_usec = timeout * 1000000; // fractions of a second to us
+ int ret = setsockopt(m_lsd, SOL_SOCKET, SO_RCVTIMEO,
+ reinterpret_cast<char*>(&tv), sizeof(tv));
+ if (ret < 0) WPI_ERROR(m_logger, "set timeout failed");
+ return ret;
+}
diff --git a/wpiutil/src/main/native/cpp/WebSocket.cpp b/wpiutil/src/main/native/cpp/WebSocket.cpp
new file mode 100644
index 0000000..82c83bb
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/WebSocket.cpp
@@ -0,0 +1,568 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/WebSocket.h"
+
+#include <random>
+
+#include "wpi/Base64.h"
+#include "wpi/HttpParser.h"
+#include "wpi/SmallString.h"
+#include "wpi/SmallVector.h"
+#include "wpi/raw_uv_ostream.h"
+#include "wpi/sha1.h"
+#include "wpi/uv/Stream.h"
+
+using namespace wpi;
+
+namespace {
+class WebSocketWriteReq : public uv::WriteReq {
+ public:
+ explicit WebSocketWriteReq(
+ std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
+ finish.connect([=](uv::Error err) {
+ MutableArrayRef<uv::Buffer> bufs{m_bufs};
+ for (auto&& buf : bufs.slice(0, m_startUser)) buf.Deallocate();
+ callback(bufs.slice(m_startUser), err);
+ });
+ }
+
+ SmallVector<uv::Buffer, 4> m_bufs;
+ size_t m_startUser;
+};
+} // namespace
+
+class WebSocket::ClientHandshakeData {
+ public:
+ ClientHandshakeData() {
+ // key is a random nonce
+ static std::random_device rd;
+ static std::default_random_engine gen{rd()};
+ std::uniform_int_distribution<unsigned int> dist(0, 255);
+ char nonce[16]; // the nonce sent to the server
+ for (char& v : nonce) v = static_cast<char>(dist(gen));
+ raw_svector_ostream os(key);
+ Base64Encode(os, StringRef{nonce, 16});
+ }
+ ~ClientHandshakeData() {
+ if (auto t = timer.lock()) {
+ t->Stop();
+ t->Close();
+ }
+ }
+
+ SmallString<64> key; // the key sent to the server
+ SmallVector<std::string, 2> protocols; // valid protocols
+ HttpParser parser{HttpParser::kResponse}; // server response parser
+ bool hasUpgrade = false;
+ bool hasConnection = false;
+ bool hasAccept = false;
+ bool hasProtocol = false;
+
+ std::weak_ptr<uv::Timer> timer;
+};
+
+static StringRef AcceptHash(StringRef key, SmallVectorImpl<char>& buf) {
+ SHA1 hash;
+ hash.Update(key);
+ hash.Update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ SmallString<64> hashBuf;
+ return Base64Encode(hash.RawFinal(hashBuf), buf);
+}
+
+WebSocket::WebSocket(uv::Stream& stream, bool server, const private_init&)
+ : m_stream{stream}, m_server{server} {
+ // Connect closed and error signals to ourselves
+ m_stream.closed.connect([this]() { SetClosed(1006, "handle closed"); });
+ m_stream.error.connect([this](uv::Error err) {
+ Terminate(1006, "stream error: " + Twine(err.name()));
+ });
+
+ // Start reading
+ m_stream.StopRead(); // we may have been reading
+ m_stream.StartRead();
+ m_stream.data.connect(
+ [this](uv::Buffer& buf, size_t size) { HandleIncoming(buf, size); });
+ m_stream.end.connect(
+ [this]() { Terminate(1006, "remote end closed connection"); });
+}
+
+WebSocket::~WebSocket() {}
+
+std::shared_ptr<WebSocket> WebSocket::CreateClient(
+ uv::Stream& stream, const Twine& uri, const Twine& host,
+ ArrayRef<StringRef> protocols, const ClientOptions& options) {
+ auto ws = std::make_shared<WebSocket>(stream, false, private_init{});
+ stream.SetData(ws);
+ ws->StartClient(uri, host, protocols, options);
+ return ws;
+}
+
+std::shared_ptr<WebSocket> WebSocket::CreateServer(uv::Stream& stream,
+ StringRef key,
+ StringRef version,
+ StringRef protocol) {
+ auto ws = std::make_shared<WebSocket>(stream, true, private_init{});
+ stream.SetData(ws);
+ ws->StartServer(key, version, protocol);
+ return ws;
+}
+
+void WebSocket::Close(uint16_t code, const Twine& reason) {
+ SendClose(code, reason);
+ if (m_state != FAILED && m_state != CLOSED) m_state = CLOSING;
+}
+
+void WebSocket::Fail(uint16_t code, const Twine& reason) {
+ if (m_state == FAILED || m_state == CLOSED) return;
+ SendClose(code, reason);
+ SetClosed(code, reason, true);
+ Shutdown();
+}
+
+void WebSocket::Terminate(uint16_t code, const Twine& reason) {
+ if (m_state == FAILED || m_state == CLOSED) return;
+ SetClosed(code, reason);
+ Shutdown();
+}
+
+void WebSocket::StartClient(const Twine& uri, const Twine& host,
+ ArrayRef<StringRef> protocols,
+ const ClientOptions& options) {
+ // Create client handshake data
+ m_clientHandshake = std::make_unique<ClientHandshakeData>();
+
+ // Build client request
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os{bufs, 4096};
+
+ os << "GET " << uri << " HTTP/1.1\r\n";
+ os << "Host: " << host << "\r\n";
+ os << "Upgrade: websocket\r\n";
+ os << "Connection: Upgrade\r\n";
+ os << "Sec-WebSocket-Key: " << m_clientHandshake->key << "\r\n";
+ os << "Sec-WebSocket-Version: 13\r\n";
+
+ // protocols (if provided)
+ if (!protocols.empty()) {
+ os << "Sec-WebSocket-Protocol: ";
+ bool first = true;
+ for (auto protocol : protocols) {
+ if (!first)
+ os << ", ";
+ else
+ first = false;
+ os << protocol;
+ // also save for later checking against server response
+ m_clientHandshake->protocols.emplace_back(protocol);
+ }
+ os << "\r\n";
+ }
+
+ // other headers
+ for (auto&& header : options.extraHeaders)
+ os << header.first << ": " << header.second << "\r\n";
+
+ // finish headers
+ os << "\r\n";
+
+ // Send client request
+ m_stream.Write(bufs, [](auto bufs, uv::Error) {
+ for (auto& buf : bufs) buf.Deallocate();
+ });
+
+ // Set up client response handling
+ m_clientHandshake->parser.status.connect([this](StringRef status) {
+ unsigned int code = m_clientHandshake->parser.GetStatusCode();
+ if (code != 101) Terminate(code, status);
+ });
+ m_clientHandshake->parser.header.connect(
+ [this](StringRef name, StringRef value) {
+ value = value.trim();
+ if (name.equals_lower("upgrade")) {
+ if (!value.equals_lower("websocket"))
+ return Terminate(1002, "invalid upgrade response value");
+ m_clientHandshake->hasUpgrade = true;
+ } else if (name.equals_lower("connection")) {
+ if (!value.equals_lower("upgrade"))
+ return Terminate(1002, "invalid connection response value");
+ m_clientHandshake->hasConnection = true;
+ } else if (name.equals_lower("sec-websocket-accept")) {
+ // Check against expected response
+ SmallString<64> acceptBuf;
+ if (!value.equals(AcceptHash(m_clientHandshake->key, acceptBuf)))
+ return Terminate(1002, "invalid accept key");
+ m_clientHandshake->hasAccept = true;
+ } else if (name.equals_lower("sec-websocket-extensions")) {
+ // No extensions are supported
+ if (!value.empty()) return Terminate(1010, "unsupported extension");
+ } else if (name.equals_lower("sec-websocket-protocol")) {
+ // Make sure it was one of the provided protocols
+ bool match = false;
+ for (auto&& protocol : m_clientHandshake->protocols) {
+ if (value.equals_lower(protocol)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) return Terminate(1003, "unsupported protocol");
+ m_clientHandshake->hasProtocol = true;
+ m_protocol = value;
+ }
+ });
+ m_clientHandshake->parser.headersComplete.connect([this](bool) {
+ if (!m_clientHandshake->hasUpgrade || !m_clientHandshake->hasConnection ||
+ !m_clientHandshake->hasAccept ||
+ (!m_clientHandshake->hasProtocol &&
+ !m_clientHandshake->protocols.empty())) {
+ return Terminate(1002, "invalid response");
+ }
+ if (m_state == CONNECTING) {
+ m_state = OPEN;
+ open(m_protocol);
+ }
+ });
+
+ // Start handshake timer if a timeout was specified
+ if (options.handshakeTimeout != (uv::Timer::Time::max)()) {
+ auto timer = uv::Timer::Create(m_stream.GetLoopRef());
+ timer->timeout.connect(
+ [this]() { Terminate(1006, "connection timed out"); });
+ timer->Start(options.handshakeTimeout);
+ m_clientHandshake->timer = timer;
+ }
+}
+
+void WebSocket::StartServer(StringRef key, StringRef version,
+ StringRef protocol) {
+ m_protocol = protocol;
+
+ // Build server response
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os{bufs, 4096};
+
+ // Handle unsupported version
+ if (version != "13") {
+ os << "HTTP/1.1 426 Upgrade Required\r\n";
+ os << "Upgrade: WebSocket\r\n";
+ os << "Sec-WebSocket-Version: 13\r\n\r\n";
+ m_stream.Write(bufs, [this](auto bufs, uv::Error) {
+ for (auto& buf : bufs) buf.Deallocate();
+ // XXX: Should we support sending a new handshake on the same connection?
+ // XXX: "this->" is required by GCC 5.5 (bug)
+ this->Terminate(1003, "unsupported protocol version");
+ });
+ return;
+ }
+
+ os << "HTTP/1.1 101 Switching Protocols\r\n";
+ os << "Upgrade: websocket\r\n";
+ os << "Connection: Upgrade\r\n";
+
+ // accept hash
+ SmallString<64> acceptBuf;
+ os << "Sec-WebSocket-Accept: " << AcceptHash(key, acceptBuf) << "\r\n";
+
+ if (!protocol.empty()) os << "Sec-WebSocket-Protocol: " << protocol << "\r\n";
+
+ // end headers
+ os << "\r\n";
+
+ // Send server response
+ m_stream.Write(bufs, [this](auto bufs, uv::Error) {
+ for (auto& buf : bufs) buf.Deallocate();
+ if (m_state == CONNECTING) {
+ m_state = OPEN;
+ open(m_protocol);
+ }
+ });
+}
+
+void WebSocket::SendClose(uint16_t code, const Twine& reason) {
+ SmallVector<uv::Buffer, 4> bufs;
+ if (code != 1005) {
+ raw_uv_ostream os{bufs, 4096};
+ const uint8_t codeMsb[] = {static_cast<uint8_t>((code >> 8) & 0xff),
+ static_cast<uint8_t>(code & 0xff)};
+ os << ArrayRef<uint8_t>(codeMsb);
+ reason.print(os);
+ }
+ Send(kFlagFin | kOpClose, bufs, [](auto bufs, uv::Error) {
+ for (auto&& buf : bufs) buf.Deallocate();
+ });
+}
+
+void WebSocket::SetClosed(uint16_t code, const Twine& reason, bool failed) {
+ if (m_state == FAILED || m_state == CLOSED) return;
+ m_state = failed ? FAILED : CLOSED;
+ SmallString<64> reasonBuf;
+ closed(code, reason.toStringRef(reasonBuf));
+}
+
+void WebSocket::Shutdown() {
+ m_stream.Shutdown([this] { m_stream.Close(); });
+}
+
+void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) {
+ // ignore incoming data if we're failed or closed
+ if (m_state == FAILED || m_state == CLOSED) return;
+
+ StringRef data{buf.base, size};
+
+ // Handle connecting state (mainly on client)
+ if (m_state == CONNECTING) {
+ if (m_clientHandshake) {
+ data = m_clientHandshake->parser.Execute(data);
+ // check for parser failure
+ if (m_clientHandshake->parser.HasError())
+ return Terminate(1003, "invalid response");
+ if (m_state != OPEN) return; // not done with handshake yet
+
+ // we're done with the handshake, so release its memory
+ m_clientHandshake.reset();
+
+ // fall through to process additional data after handshake
+ } else {
+ return Terminate(1003, "got data on server before response");
+ }
+ }
+
+ // Message processing
+ while (!data.empty()) {
+ if (m_frameSize == UINT64_MAX) {
+ // Need at least two bytes to determine header length
+ if (m_header.size() < 2u) {
+ size_t toCopy = (std::min)(2u - m_header.size(), data.size());
+ m_header.append(data.bytes_begin(), data.bytes_begin() + toCopy);
+ data = data.drop_front(toCopy);
+ if (m_header.size() < 2u) return; // need more data
+
+ // Validate RSV bits are zero
+ if ((m_header[0] & 0x70) != 0) return Fail(1002, "nonzero RSV");
+ }
+
+ // Once we have first two bytes, we can calculate the header size
+ if (m_headerSize == 0) {
+ m_headerSize = 2;
+ uint8_t len = m_header[1] & kLenMask;
+ if (len == 126)
+ m_headerSize += 2;
+ else if (len == 127)
+ m_headerSize += 8;
+ bool masking = (m_header[1] & kFlagMasking) != 0;
+ if (masking) m_headerSize += 4; // masking key
+ // On server side, incoming messages MUST be masked
+ // On client side, incoming messages MUST NOT be masked
+ if (m_server && !masking) return Fail(1002, "client data not masked");
+ if (!m_server && masking) return Fail(1002, "server data masked");
+ }
+
+ // Need to complete header to calculate message size
+ if (m_header.size() < m_headerSize) {
+ size_t toCopy = (std::min)(m_headerSize - m_header.size(), data.size());
+ m_header.append(data.bytes_begin(), data.bytes_begin() + toCopy);
+ data = data.drop_front(toCopy);
+ if (m_header.size() < m_headerSize) return; // need more data
+ }
+
+ if (m_header.size() >= m_headerSize) {
+ // get payload length
+ uint8_t len = m_header[1] & kLenMask;
+ if (len == 126)
+ m_frameSize = (static_cast<uint16_t>(m_header[2]) << 8) |
+ static_cast<uint16_t>(m_header[3]);
+ else if (len == 127)
+ m_frameSize = (static_cast<uint64_t>(m_header[2]) << 56) |
+ (static_cast<uint64_t>(m_header[3]) << 48) |
+ (static_cast<uint64_t>(m_header[4]) << 40) |
+ (static_cast<uint64_t>(m_header[5]) << 32) |
+ (static_cast<uint64_t>(m_header[6]) << 24) |
+ (static_cast<uint64_t>(m_header[7]) << 16) |
+ (static_cast<uint64_t>(m_header[8]) << 8) |
+ static_cast<uint64_t>(m_header[9]);
+ else
+ m_frameSize = len;
+
+ // limit maximum size
+ if ((m_payload.size() + m_frameSize) > m_maxMessageSize)
+ return Fail(1009, "message too large");
+ }
+ }
+
+ if (m_frameSize != UINT64_MAX) {
+ size_t need = m_frameStart + m_frameSize - m_payload.size();
+ size_t toCopy = (std::min)(need, data.size());
+ m_payload.append(data.bytes_begin(), data.bytes_begin() + toCopy);
+ data = data.drop_front(toCopy);
+ need -= toCopy;
+ if (need == 0) {
+ // We have a complete frame
+ // If the message had masking, unmask it
+ if ((m_header[1] & kFlagMasking) != 0) {
+ uint8_t key[4] = {
+ m_header[m_headerSize - 4], m_header[m_headerSize - 3],
+ m_header[m_headerSize - 2], m_header[m_headerSize - 1]};
+ int n = 0;
+ for (uint8_t& ch :
+ MutableArrayRef<uint8_t>{m_payload}.slice(m_frameStart)) {
+ ch ^= key[n++];
+ if (n >= 4) n = 0;
+ }
+ }
+
+ // Handle message
+ bool fin = (m_header[0] & kFlagFin) != 0;
+ uint8_t opcode = m_header[0] & kOpMask;
+ switch (opcode) {
+ case kOpCont:
+ switch (m_fragmentOpcode) {
+ case kOpText:
+ if (!m_combineFragments || fin)
+ text(StringRef{reinterpret_cast<char*>(m_payload.data()),
+ m_payload.size()},
+ fin);
+ break;
+ case kOpBinary:
+ if (!m_combineFragments || fin) binary(m_payload, fin);
+ break;
+ default:
+ // no preceding message?
+ return Fail(1002, "invalid continuation message");
+ }
+ if (fin) m_fragmentOpcode = 0;
+ break;
+ case kOpText:
+ if (m_fragmentOpcode != 0) return Fail(1002, "incomplete fragment");
+ if (!m_combineFragments || fin)
+ text(StringRef{reinterpret_cast<char*>(m_payload.data()),
+ m_payload.size()},
+ fin);
+ if (!fin) m_fragmentOpcode = opcode;
+ break;
+ case kOpBinary:
+ if (m_fragmentOpcode != 0) return Fail(1002, "incomplete fragment");
+ if (!m_combineFragments || fin) binary(m_payload, fin);
+ if (!fin) m_fragmentOpcode = opcode;
+ break;
+ case kOpClose: {
+ uint16_t code;
+ StringRef reason;
+ if (!fin) {
+ code = 1002;
+ reason = "cannot fragment control frames";
+ } else if (m_payload.size() < 2) {
+ code = 1005;
+ } else {
+ code = (static_cast<uint16_t>(m_payload[0]) << 8) |
+ static_cast<uint16_t>(m_payload[1]);
+ reason = StringRef{reinterpret_cast<char*>(m_payload.data()),
+ m_payload.size()}
+ .drop_front(2);
+ }
+ // Echo the close if we didn't previously send it
+ if (m_state != CLOSING) SendClose(code, reason);
+ SetClosed(code, reason);
+ // If we're the server, shutdown the connection.
+ if (m_server) Shutdown();
+ break;
+ }
+ case kOpPing:
+ if (!fin) return Fail(1002, "cannot fragment control frames");
+ ping(m_payload);
+ break;
+ case kOpPong:
+ if (!fin) return Fail(1002, "cannot fragment control frames");
+ pong(m_payload);
+ break;
+ default:
+ return Fail(1002, "invalid message opcode");
+ }
+
+ // Prepare for next message
+ m_header.clear();
+ m_headerSize = 0;
+ if (!m_combineFragments || fin) m_payload.clear();
+ m_frameStart = m_payload.size();
+ m_frameSize = UINT64_MAX;
+ }
+ }
+ }
+}
+
+void WebSocket::Send(
+ uint8_t opcode, ArrayRef<uv::Buffer> data,
+ std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
+ // If we're not open, emit an error and don't send the data
+ if (m_state != OPEN) {
+ int err;
+ if (m_state == CONNECTING)
+ err = UV_EAGAIN;
+ else
+ err = UV_ESHUTDOWN;
+ SmallVector<uv::Buffer, 4> bufs{data.begin(), data.end()};
+ callback(bufs, uv::Error{err});
+ return;
+ }
+
+ auto req = std::make_shared<WebSocketWriteReq>(callback);
+ raw_uv_ostream os{req->m_bufs, 4096};
+
+ // opcode (includes FIN bit)
+ os << static_cast<unsigned char>(opcode);
+
+ // payload length
+ uint64_t size = 0;
+ for (auto&& buf : data) size += buf.len;
+ if (size < 126) {
+ os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | size);
+ } else if (size <= 0xffff) {
+ os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | 126);
+ const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 8) & 0xff),
+ static_cast<uint8_t>(size & 0xff)};
+ os << ArrayRef<uint8_t>(sizeMsb);
+ } else {
+ os << static_cast<unsigned char>((m_server ? 0x00 : kFlagMasking) | 127);
+ const uint8_t sizeMsb[] = {static_cast<uint8_t>((size >> 56) & 0xff),
+ static_cast<uint8_t>((size >> 48) & 0xff),
+ static_cast<uint8_t>((size >> 40) & 0xff),
+ static_cast<uint8_t>((size >> 32) & 0xff),
+ static_cast<uint8_t>((size >> 24) & 0xff),
+ static_cast<uint8_t>((size >> 16) & 0xff),
+ static_cast<uint8_t>((size >> 8) & 0xff),
+ static_cast<uint8_t>(size & 0xff)};
+ os << ArrayRef<uint8_t>(sizeMsb);
+ }
+
+ // clients need to mask the input data
+ if (!m_server) {
+ // generate masking key
+ static std::random_device rd;
+ static std::default_random_engine gen{rd()};
+ std::uniform_int_distribution<unsigned int> dist(0, 255);
+ uint8_t key[4];
+ for (uint8_t& v : key) v = dist(gen);
+ os << ArrayRef<uint8_t>{key, 4};
+ // copy and mask data
+ int n = 0;
+ for (auto&& buf : data) {
+ for (auto&& ch : buf.data()) {
+ os << static_cast<unsigned char>(static_cast<uint8_t>(ch) ^ key[n++]);
+ if (n >= 4) n = 0;
+ }
+ }
+ req->m_startUser = req->m_bufs.size();
+ req->m_bufs.append(data.begin(), data.end());
+ // don't send the user bufs as we copied their data
+ m_stream.Write(ArrayRef<uv::Buffer>{req->m_bufs}.slice(0, req->m_startUser),
+ req);
+ } else {
+ // servers can just send the buffers directly without masking
+ req->m_startUser = req->m_bufs.size();
+ req->m_bufs.append(data.begin(), data.end());
+ m_stream.Write(req->m_bufs, req);
+ }
+}
diff --git a/wpiutil/src/main/native/cpp/WebSocketServer.cpp b/wpiutil/src/main/native/cpp/WebSocketServer.cpp
new file mode 100644
index 0000000..056cb62
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/WebSocketServer.cpp
@@ -0,0 +1,143 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/WebSocketServer.h"
+
+#include "wpi/raw_uv_ostream.h"
+#include "wpi/uv/Buffer.h"
+#include "wpi/uv/Stream.h"
+
+using namespace wpi;
+
+WebSocketServerHelper::WebSocketServerHelper(HttpParser& req) {
+ req.header.connect([this](StringRef name, StringRef value) {
+ if (name.equals_lower("host")) {
+ m_gotHost = true;
+ } else if (name.equals_lower("upgrade")) {
+ if (value.equals_lower("websocket")) m_websocket = true;
+ } else if (name.equals_lower("sec-websocket-key")) {
+ m_key = value;
+ } else if (name.equals_lower("sec-websocket-version")) {
+ m_version = value;
+ } else if (name.equals_lower("sec-websocket-protocol")) {
+ // Protocols are comma delimited, repeated headers add to list
+ SmallVector<StringRef, 2> protocols;
+ value.split(protocols, ",", -1, false);
+ for (auto protocol : protocols) {
+ protocol = protocol.trim();
+ if (!protocol.empty()) m_protocols.emplace_back(protocol);
+ }
+ }
+ });
+ req.headersComplete.connect([&req, this](bool) {
+ if (req.IsUpgrade() && IsUpgrade()) upgrade();
+ });
+}
+
+std::pair<bool, StringRef> WebSocketServerHelper::MatchProtocol(
+ ArrayRef<StringRef> protocols) {
+ if (protocols.empty() && m_protocols.empty())
+ return std::make_pair(true, StringRef{});
+ for (auto protocol : protocols) {
+ for (auto&& clientProto : m_protocols) {
+ if (protocol == clientProto) return std::make_pair(true, protocol);
+ }
+ }
+ return std::make_pair(false, StringRef{});
+}
+
+WebSocketServer::WebSocketServer(uv::Stream& stream,
+ ArrayRef<StringRef> protocols,
+ const ServerOptions& options,
+ const private_init&)
+ : m_stream{stream},
+ m_helper{m_req},
+ m_protocols{protocols.begin(), protocols.end()},
+ m_options{options} {
+ // Header handling
+ m_req.header.connect([this](StringRef name, StringRef value) {
+ if (name.equals_lower("host")) {
+ if (m_options.checkHost) {
+ if (!m_options.checkHost(value)) Abort(401, "Unrecognized Host");
+ }
+ }
+ });
+ m_req.url.connect([this](StringRef name) {
+ if (m_options.checkUrl) {
+ if (!m_options.checkUrl(name)) Abort(404, "Not Found");
+ }
+ });
+ m_req.headersComplete.connect([this](bool) {
+ // We only accept websocket connections
+ if (!m_helper.IsUpgrade() || !m_req.IsUpgrade())
+ Abort(426, "Upgrade Required");
+ });
+
+ // Handle upgrade event
+ m_helper.upgrade.connect([this] {
+ if (m_aborted) return;
+
+ // Negotiate sub-protocol
+ SmallVector<StringRef, 2> protocols{m_protocols.begin(), m_protocols.end()};
+ StringRef protocol = m_helper.MatchProtocol(protocols).second;
+
+ // Disconnect our header reader
+ m_dataConn.disconnect();
+
+ // Accepting the stream may destroy this (as it replaces the stream user
+ // data), so grab a shared pointer first.
+ auto self = shared_from_this();
+
+ // Accept the upgrade
+ auto ws = m_helper.Accept(m_stream, protocol);
+
+ // Connect the websocket open event to our connected event.
+ ws->open.connect_extended([self, s = ws.get()](auto conn, StringRef) {
+ self->connected(self->m_req.GetUrl(), *s);
+ conn.disconnect(); // one-shot
+ });
+ });
+
+ // Set up stream
+ stream.StartRead();
+ m_dataConn =
+ stream.data.connect_connection([this](uv::Buffer& buf, size_t size) {
+ if (m_aborted) return;
+ m_req.Execute(StringRef{buf.base, size});
+ if (m_req.HasError()) Abort(400, "Bad Request");
+ });
+ m_errorConn =
+ stream.error.connect_connection([this](uv::Error) { m_stream.Close(); });
+ m_endConn = stream.end.connect_connection([this] { m_stream.Close(); });
+}
+
+std::shared_ptr<WebSocketServer> WebSocketServer::Create(
+ uv::Stream& stream, ArrayRef<StringRef> protocols,
+ const ServerOptions& options) {
+ auto server = std::make_shared<WebSocketServer>(stream, protocols, options,
+ private_init{});
+ stream.SetData(server);
+ return server;
+}
+
+void WebSocketServer::Abort(uint16_t code, StringRef reason) {
+ if (m_aborted) return;
+ m_aborted = true;
+
+ // Build response
+ SmallVector<uv::Buffer, 4> bufs;
+ raw_uv_ostream os{bufs, 1024};
+
+ // Handle unsupported version
+ os << "HTTP/1.1 " << code << ' ' << reason << "\r\n";
+ if (code == 426) os << "Upgrade: WebSocket\r\n";
+ os << "\r\n";
+ m_stream.Write(bufs, [this](auto bufs, uv::Error) {
+ for (auto& buf : bufs) buf.Deallocate();
+ m_stream.Shutdown([this] { m_stream.Close(); });
+ });
+}
diff --git a/wpiutil/src/main/native/cpp/future.cpp b/wpiutil/src/main/native/cpp/future.cpp
new file mode 100644
index 0000000..7ce0875
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/future.cpp
@@ -0,0 +1,119 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/future.h"
+
+namespace wpi {
+namespace detail {
+
+PromiseFactoryBase::~PromiseFactoryBase() {
+ m_active = false;
+ m_resultCv.notify_all(); // wake up any waiters
+}
+
+void PromiseFactoryBase::IgnoreResult(uint64_t request) {
+ std::unique_lock lock(m_resultMutex);
+ EraseRequest(request);
+}
+
+uint64_t PromiseFactoryBase::CreateRequest() {
+ std::unique_lock lock(m_resultMutex);
+ uint64_t req = ++m_uid;
+ m_requests.push_back(req);
+ return req;
+}
+
+bool PromiseFactoryBase::EraseRequest(uint64_t request) {
+ if (request == 0) return false;
+ auto it = std::find_if(m_requests.begin(), m_requests.end(),
+ [=](auto r) { return r == request; });
+ if (it == m_requests.end()) return false; // no waiters
+ m_requests.erase(it);
+ return true;
+}
+
+} // namespace detail
+
+future<void> PromiseFactory<void>::MakeReadyFuture() {
+ std::unique_lock lock(GetResultMutex());
+ uint64_t req = CreateErasedRequest();
+ m_results.emplace_back(req);
+ return future<void>{this, req};
+}
+
+void PromiseFactory<void>::SetValue(uint64_t request) {
+ std::unique_lock lock(GetResultMutex());
+ if (!EraseRequest(request)) return;
+ auto it = std::find_if(m_thens.begin(), m_thens.end(),
+ [=](const auto& x) { return x.request == request; });
+ if (it != m_thens.end()) {
+ uint64_t outRequest = it->outRequest;
+ ThenFunction func = std::move(it->func);
+ m_thens.erase(it);
+ lock.unlock();
+ return func(outRequest);
+ }
+ m_results.emplace_back(request);
+ Notify();
+}
+
+void PromiseFactory<void>::SetThen(uint64_t request, uint64_t outRequest,
+ ThenFunction func) {
+ std::unique_lock lock(GetResultMutex());
+ auto it = std::find_if(m_results.begin(), m_results.end(),
+ [=](const auto& r) { return r == request; });
+ if (it != m_results.end()) {
+ m_results.erase(it);
+ lock.unlock();
+ return func(outRequest);
+ }
+ m_thens.emplace_back(request, outRequest, func);
+}
+
+bool PromiseFactory<void>::IsReady(uint64_t request) noexcept {
+ std::unique_lock lock(GetResultMutex());
+ auto it = std::find_if(m_results.begin(), m_results.end(),
+ [=](const auto& r) { return r == request; });
+ return it != m_results.end();
+}
+
+void PromiseFactory<void>::GetResult(uint64_t request) {
+ // wait for response
+ std::unique_lock lock(GetResultMutex());
+ while (IsActive()) {
+ // Did we get a response to *our* request?
+ auto it = std::find_if(m_results.begin(), m_results.end(),
+ [=](const auto& r) { return r == request; });
+ if (it != m_results.end()) {
+ // Yes, remove it from the vector and we're done.
+ m_results.erase(it);
+ return;
+ }
+ // No, keep waiting for a response
+ Wait(lock);
+ }
+}
+
+void PromiseFactory<void>::WaitResult(uint64_t request) {
+ // wait for response
+ std::unique_lock lock(GetResultMutex());
+ while (IsActive()) {
+ // Did we get a response to *our* request?
+ auto it = std::find_if(m_results.begin(), m_results.end(),
+ [=](const auto& r) { return r == request; });
+ if (it != m_results.end()) return;
+ // No, keep waiting for a response
+ Wait(lock);
+ }
+}
+
+PromiseFactory<void>& PromiseFactory<void>::GetInstance() {
+ static PromiseFactory<void> inst;
+ return inst;
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/hostname.cpp b/wpiutil/src/main/native/cpp/hostname.cpp
new file mode 100644
index 0000000..ffc5cad
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/hostname.cpp
@@ -0,0 +1,56 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/hostname.h"
+
+#include <cstdlib>
+#include <string>
+
+#include "uv.h"
+#include "wpi/SmallVector.h"
+#include "wpi/StringRef.h"
+
+namespace wpi {
+
+std::string GetHostname() {
+ std::string rv;
+ char name[256];
+ size_t size = sizeof(name);
+
+ int err = uv_os_gethostname(name, &size);
+ if (err == 0) {
+ rv.assign(name, size);
+ } else if (err == UV_ENOBUFS) {
+ char* name2 = static_cast<char*>(std::malloc(size));
+ err = uv_os_gethostname(name2, &size);
+ if (err == 0) rv.assign(name2, size);
+ std::free(name2);
+ }
+
+ return rv;
+}
+
+StringRef GetHostname(SmallVectorImpl<char>& name) {
+ // Use a tmp array to not require the SmallVector to be too large.
+ char tmpName[256];
+ size_t size = sizeof(tmpName);
+
+ name.clear();
+
+ int err = uv_os_gethostname(tmpName, &size);
+ if (err == 0) {
+ name.append(tmpName, tmpName + size);
+ } else if (err == UV_ENOBUFS) {
+ name.resize(size);
+ err = uv_os_gethostname(name.data(), &size);
+ if (err != 0) size = 0;
+ }
+
+ return StringRef{name.data(), size};
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/http_parser.cpp b/wpiutil/src/main/native/cpp/http_parser.cpp
new file mode 100644
index 0000000..fb56010
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/http_parser.cpp
@@ -0,0 +1,2475 @@
+/* Copyright Joyent, Inc. and other Node contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "wpi/http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef _WIN32
+#pragma warning(disable : 4018 26451)
+#endif
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i) \
+ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
+ (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e) \
+do { \
+ parser->nread = nread; \
+ parser->http_errno = (e); \
+} while(0)
+
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (enum state) (V);
+#define RETURN(V) \
+do { \
+ parser->nread = nread; \
+ parser->state = CURRENT_STATE(); \
+ return (V); \
+} while (0);
+#define REEXECUTE() \
+ goto reexecute; \
+
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != settings->on_##FOR(parser))) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ UPDATE_STATE(parser->state); \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
+ return (ER); \
+ } \
+ } \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (FOR##_mark) { \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != \
+ settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ UPDATE_STATE(parser->state); \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
+ return (ER); \
+ } \
+ } \
+ FOR##_mark = NULL; \
+ } \
+} while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR) \
+do { \
+ if (!FOR##_mark) { \
+ FOR##_mark = p; \
+ } \
+} while (0)
+
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V) \
+do { \
+ nread += (V); \
+ if (UNLIKELY(nread > (HTTP_MAX_HEADER_SIZE))) { \
+ SET_ERRNO(HPE_HEADER_OVERFLOW); \
+ goto error; \
+ } \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+namespace wpi {
+
+
+static const char *method_strings[] =
+ {
+#define XX(num, name, string) #string,
+ HTTP_METHOD_MAP(XX)
+#undef XX
+ };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ ' ', '!', 0, '#', '$', '%', '&', '\'',
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 0, 0, '*', '+', 0, '-', '.', 0,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ '0', '1', '2', '3', '4', '5', '6', '7',
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ '8', '9', 0, 0, 0, 0, 0, 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 'x', 'y', 'z', 0, 0, 0, '^', '_',
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 'x', 'y', 'z', 0, '|', 0, '~', 0 };
+
+
+static const int8_t unhex[256] =
+ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ };
+
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
+
+#undef T
+
+enum state
+ { s_dead = 1 /* important that this is > 0 */
+
+ , s_start_req_or_res
+ , s_res_or_resp_H
+ , s_start_res
+ , s_res_H
+ , s_res_HT
+ , s_res_HTT
+ , s_res_HTTP
+ , s_res_http_major
+ , s_res_http_dot
+ , s_res_http_minor
+ , s_res_http_end
+ , s_res_first_status_code
+ , s_res_status_code
+ , s_res_status_start
+ , s_res_status
+ , s_res_line_almost_done
+
+ , s_start_req
+
+ , s_req_method
+ , s_req_spaces_before_url
+ , s_req_schema
+ , s_req_schema_slash
+ , s_req_schema_slash_slash
+ , s_req_server_start
+ , s_req_server
+ , s_req_server_with_at
+ , s_req_path
+ , s_req_query_string_start
+ , s_req_query_string
+ , s_req_fragment_start
+ , s_req_fragment
+ , s_req_http_start
+ , s_req_http_H
+ , s_req_http_HT
+ , s_req_http_HTT
+ , s_req_http_HTTP
+ , s_req_http_major
+ , s_req_http_dot
+ , s_req_http_minor
+ , s_req_http_end
+ , s_req_line_almost_done
+
+ , s_header_field_start
+ , s_header_field
+ , s_header_value_discard_ws
+ , s_header_value_discard_ws_almost_done
+ , s_header_value_discard_lws
+ , s_header_value_start
+ , s_header_value
+ , s_header_value_lws
+
+ , s_header_almost_done
+
+ , s_chunk_size_start
+ , s_chunk_size
+ , s_chunk_parameters
+ , s_chunk_size_almost_done
+
+ , s_headers_almost_done
+ , s_headers_done
+
+ /* Important: 's_headers_done' must be the last 'header' state. All
+ * states beyond this must be 'body' states. It is used for overflow
+ * checking. See the PARSING_HEADER() macro.
+ */
+
+ , s_chunk_data
+ , s_chunk_data_almost_done
+ , s_chunk_data_done
+
+ , s_body_identity
+ , s_body_identity_eof
+
+ , s_message_done
+ };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+ { h_general = 0
+ , h_C
+ , h_CO
+ , h_CON
+
+ , h_matching_connection
+ , h_matching_proxy_connection
+ , h_matching_content_length
+ , h_matching_transfer_encoding
+ , h_matching_upgrade
+
+ , h_connection
+ , h_content_length
+ , h_content_length_num
+ , h_content_length_ws
+ , h_transfer_encoding
+ , h_upgrade
+
+ , h_matching_transfer_encoding_chunked
+ , h_matching_connection_token_start
+ , h_matching_connection_keep_alive
+ , h_matching_connection_close
+ , h_matching_connection_upgrade
+ , h_matching_connection_token
+
+ , h_transfer_encoding_chunked
+ , h_connection_keep_alive
+ , h_connection_close
+ , h_connection_upgrade
+ };
+
+enum http_host_state
+ {
+ s_http_host_dead = 1
+ , s_http_userinfo_start
+ , s_http_userinfo
+ , s_http_host_start
+ , s_http_host_v6_start
+ , s_http_host
+ , s_http_host_v6
+ , s_http_host_v6_end
+ , s_http_host_v6_zone_start
+ , s_http_host_v6_zone
+ , s_http_host_port_start
+ , s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode */
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
+ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+ (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+ (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c) STRICT_TOKEN(c)
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c) tokens[(unsigned char)c]
+#define IS_URL_CHAR(c) \
+ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c) \
+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/**
+ * Verify that a char is a valid visible (printable) US-ASCII
+ * character or %x80-FF
+ **/
+#define IS_HEADER_CHAR(ch) \
+ (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond) \
+do { \
+ if (cond) { \
+ SET_ERRNO(HPE_STRICT); \
+ goto error; \
+ } \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+ const char *name;
+ const char *description;
+} http_strerror_tab[] = {
+ HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+ if (ch == ' ' || ch == '\r' || ch == '\n') {
+ return s_dead;
+ }
+
+#if HTTP_PARSER_STRICT
+ if (ch == '\t' || ch == '\f') {
+ return s_dead;
+ }
+#endif
+
+ switch (s) {
+ case s_req_spaces_before_url:
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
+ * All methods except CONNECT are followed by '/' or '*'.
+ */
+
+ if (ch == '/' || ch == '*') {
+ return s_req_path;
+ }
+
+ if (IS_ALPHA(ch)) {
+ return s_req_schema;
+ }
+
+ break;
+
+ case s_req_schema:
+ if (IS_ALPHA(ch)) {
+ return s;
+ }
+
+ if (ch == ':') {
+ return s_req_schema_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash:
+ if (ch == '/') {
+ return s_req_schema_slash_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash_slash:
+ if (ch == '/') {
+ return s_req_server_start;
+ }
+
+ break;
+
+ case s_req_server_with_at:
+ if (ch == '@') {
+ return s_dead;
+ }
+
+ /* fall through */
+ case s_req_server_start:
+ case s_req_server:
+ if (ch == '/') {
+ return s_req_path;
+ }
+
+ if (ch == '?') {
+ return s_req_query_string_start;
+ }
+
+ if (ch == '@') {
+ return s_req_server_with_at;
+ }
+
+ if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+ return s_req_server;
+ }
+
+ break;
+
+ case s_req_path:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_query_string_start;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_query_string_start:
+ case s_req_query_string:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_query_string;
+ }
+
+ switch (ch) {
+ case '?':
+ /* allow extra '?' in query string */
+ return s_req_query_string;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_fragment_start:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_fragment;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_fragment;
+
+ case '#':
+ return s;
+ }
+
+ break;
+
+ case s_req_fragment:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ case '#':
+ return s;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* We should never fall out of the switch above unless there's an error */
+ return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+ const http_parser_settings *settings,
+ const char *data,
+ size_t len)
+{
+ char c, ch;
+ int8_t unhex_val;
+ const char *p = data;
+ const char *header_field_mark = 0;
+ const char *header_value_mark = 0;
+ const char *url_mark = 0;
+ const char *body_mark = 0;
+ const char *status_mark = 0;
+ enum state p_state = (enum state) parser->state;
+ const unsigned int lenient = parser->lenient_http_headers;
+ uint32_t nread = parser->nread;
+
+ /* We're in an error state. Don't bother doing anything. */
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ return 0;
+ }
+
+ if (len == 0) {
+ switch (CURRENT_STATE()) {
+ case s_body_identity_eof:
+ /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+ * we got paused.
+ */
+ CALLBACK_NOTIFY_NOADVANCE(message_complete);
+ return 0;
+
+ case s_dead:
+ case s_start_req_or_res:
+ case s_start_res:
+ case s_start_req:
+ return 0;
+
+ default:
+ SET_ERRNO(HPE_INVALID_EOF_STATE);
+ return 1;
+ }
+ }
+
+
+ if (CURRENT_STATE() == s_header_field)
+ header_field_mark = data;
+ if (CURRENT_STATE() == s_header_value)
+ header_value_mark = data;
+ switch (CURRENT_STATE()) {
+ case s_req_path:
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ case s_req_server:
+ case s_req_server_with_at:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ url_mark = data;
+ break;
+ case s_res_status:
+ status_mark = data;
+ break;
+ default:
+ break;
+ }
+
+ for (p=data; p != data + len; p++) {
+ ch = *p;
+
+ if (PARSING_HEADER(CURRENT_STATE()))
+ COUNT_HEADER_SIZE(1);
+
+reexecute:
+ switch (CURRENT_STATE()) {
+
+ case s_dead:
+ /* this state is used after a 'Connection: close' message
+ * the parser will error out if it reads another message
+ */
+ if (LIKELY(ch == CR || ch == LF))
+ break;
+
+ SET_ERRNO(HPE_CLOSED_CONNECTION);
+ goto error;
+
+ case s_start_req_or_res:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (ch == 'H') {
+ UPDATE_STATE(s_res_or_resp_H);
+
+ CALLBACK_NOTIFY(message_begin);
+ } else {
+ parser->type = HTTP_REQUEST;
+ UPDATE_STATE(s_start_req);
+ REEXECUTE();
+ }
+
+ break;
+ }
+
+ case s_res_or_resp_H:
+ if (ch == 'T') {
+ parser->type = HTTP_RESPONSE;
+ UPDATE_STATE(s_res_HT);
+ } else {
+ if (UNLIKELY(ch != 'E')) {
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ parser->type = HTTP_REQUEST;
+ parser->method = HTTP_HEAD;
+ parser->index = 2;
+ UPDATE_STATE(s_req_method);
+ }
+ break;
+
+ case s_start_res:
+ {
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ switch (ch) {
+ case 'H':
+ UPDATE_STATE(s_res_H);
+ break;
+
+ case CR:
+ case LF:
+ break;
+
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ CALLBACK_NOTIFY(message_begin);
+ break;
+ }
+
+ case s_res_H:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_res_HT);
+ break;
+
+ case s_res_HT:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_res_HTT);
+ break;
+
+ case s_res_HTT:
+ STRICT_CHECK(ch != 'P');
+ UPDATE_STATE(s_res_HTTP);
+ break;
+
+ case s_res_HTTP:
+ STRICT_CHECK(ch != '/');
+ UPDATE_STATE(s_res_http_major);
+ break;
+
+ case s_res_http_major:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ UPDATE_STATE(s_res_http_dot);
+ break;
+
+ case s_res_http_dot:
+ {
+ if (UNLIKELY(ch != '.')) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ UPDATE_STATE(s_res_http_minor);
+ break;
+ }
+
+ case s_res_http_minor:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ UPDATE_STATE(s_res_http_end);
+ break;
+
+ case s_res_http_end:
+ {
+ if (UNLIKELY(ch != ' ')) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ UPDATE_STATE(s_res_first_status_code);
+ break;
+ }
+
+ case s_res_first_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ if (ch == ' ') {
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ parser->status_code = ch - '0';
+ UPDATE_STATE(s_res_status_code);
+ break;
+ }
+
+ case s_res_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ switch (ch) {
+ case ' ':
+ UPDATE_STATE(s_res_status_start);
+ break;
+ case CR:
+ case LF:
+ UPDATE_STATE(s_res_status_start);
+ REEXECUTE();
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ break;
+ }
+
+ parser->status_code *= 10;
+ parser->status_code += ch - '0';
+
+ if (UNLIKELY(parser->status_code > 999)) {
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_res_status_start:
+ {
+ MARK(status);
+ UPDATE_STATE(s_res_status);
+ parser->index = 0;
+
+ if (ch == CR || ch == LF)
+ REEXECUTE();
+
+ break;
+ }
+
+ case s_res_status:
+ if (ch == CR) {
+ UPDATE_STATE(s_res_line_almost_done);
+ CALLBACK_DATA(status);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ CALLBACK_DATA(status);
+ break;
+ }
+
+ break;
+
+ case s_res_line_almost_done:
+ STRICT_CHECK(ch != LF);
+ UPDATE_STATE(s_header_field_start);
+ break;
+
+ case s_start_req:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (UNLIKELY(!IS_ALPHA(ch))) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ parser->method = (enum http_method) 0;
+ parser->index = 1;
+ switch (ch) {
+ case 'A': parser->method = HTTP_ACL; break;
+ case 'B': parser->method = HTTP_BIND; break;
+ case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+ case 'D': parser->method = HTTP_DELETE; break;
+ case 'G': parser->method = HTTP_GET; break;
+ case 'H': parser->method = HTTP_HEAD; break;
+ case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
+ case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
+ case 'N': parser->method = HTTP_NOTIFY; break;
+ case 'O': parser->method = HTTP_OPTIONS; break;
+ case 'P': parser->method = HTTP_POST;
+ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+ break;
+ case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
+ case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
+ case 'T': parser->method = HTTP_TRACE; break;
+ case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+ UPDATE_STATE(s_req_method);
+
+ CALLBACK_NOTIFY(message_begin);
+
+ break;
+ }
+
+ case s_req_method:
+ {
+ const char *matcher;
+ if (UNLIKELY(ch == '\0')) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ matcher = method_strings[parser->method];
+ if (ch == ' ' && matcher[parser->index] == '\0') {
+ UPDATE_STATE(s_req_spaces_before_url);
+ } else if (ch == matcher[parser->index]) {
+ ; /* nada */
+ } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
+
+ switch (parser->method << 16 | parser->index << 8 | ch) {
+#define XX(meth, pos, ch, new_meth) \
+ case (HTTP_##meth << 16 | pos << 8 | ch): \
+ parser->method = HTTP_##new_meth; break;
+
+ XX(POST, 1, 'U', PUT)
+ XX(POST, 1, 'A', PATCH)
+ XX(POST, 1, 'R', PROPFIND)
+ XX(PUT, 2, 'R', PURGE)
+ XX(CONNECT, 1, 'H', CHECKOUT)
+ XX(CONNECT, 2, 'P', COPY)
+ XX(MKCOL, 1, 'O', MOVE)
+ XX(MKCOL, 1, 'E', MERGE)
+ XX(MKCOL, 1, '-', MSEARCH)
+ XX(MKCOL, 2, 'A', MKACTIVITY)
+ XX(MKCOL, 3, 'A', MKCALENDAR)
+ XX(SUBSCRIBE, 1, 'E', SEARCH)
+ XX(SUBSCRIBE, 1, 'O', SOURCE)
+ XX(REPORT, 2, 'B', REBIND)
+ XX(PROPFIND, 4, 'P', PROPPATCH)
+ XX(LOCK, 1, 'I', LINK)
+ XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
+ XX(UNLOCK, 2, 'B', UNBIND)
+ XX(UNLOCK, 3, 'I', UNLINK)
+#undef XX
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+ } else {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ ++parser->index;
+ break;
+ }
+
+ case s_req_spaces_before_url:
+ {
+ if (ch == ' ') break;
+
+ MARK(url);
+ if (parser->method == HTTP_CONNECT) {
+ UPDATE_STATE(s_req_server_start);
+ }
+
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ {
+ switch (ch) {
+ /* No whitespace allowed here */
+ case ' ':
+ case CR:
+ case LF:
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ default:
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+
+ break;
+ }
+
+ case s_req_server:
+ case s_req_server_with_at:
+ case s_req_path:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ {
+ switch (ch) {
+ case ' ':
+ UPDATE_STATE(s_req_http_start);
+ CALLBACK_DATA(url);
+ break;
+ case CR:
+ case LF:
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ UPDATE_STATE((ch == CR) ?
+ s_req_line_almost_done :
+ s_header_field_start);
+ CALLBACK_DATA(url);
+ break;
+ default:
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+ break;
+ }
+
+ case s_req_http_start:
+ switch (ch) {
+ case 'H':
+ UPDATE_STATE(s_req_http_H);
+ break;
+ case ' ':
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+ break;
+
+ case s_req_http_H:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_req_http_HT);
+ break;
+
+ case s_req_http_HT:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_req_http_HTT);
+ break;
+
+ case s_req_http_HTT:
+ STRICT_CHECK(ch != 'P');
+ UPDATE_STATE(s_req_http_HTTP);
+ break;
+
+ case s_req_http_HTTP:
+ STRICT_CHECK(ch != '/');
+ UPDATE_STATE(s_req_http_major);
+ break;
+
+ case s_req_http_major:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ UPDATE_STATE(s_req_http_dot);
+ break;
+
+ case s_req_http_dot:
+ {
+ if (UNLIKELY(ch != '.')) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ UPDATE_STATE(s_req_http_minor);
+ break;
+ }
+
+ case s_req_http_minor:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ UPDATE_STATE(s_req_http_end);
+ break;
+
+ case s_req_http_end:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_req_line_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ break;
+ }
+
+ /* end of request line */
+ case s_req_line_almost_done:
+ {
+ if (UNLIKELY(ch != LF)) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ case s_header_field_start:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_headers_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ /* they might be just sending \n instead of \r\n so this would be
+ * the second \n to denote the end of headers*/
+ UPDATE_STATE(s_headers_almost_done);
+ REEXECUTE();
+ }
+
+ c = TOKEN(ch);
+
+ if (UNLIKELY(!c)) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ MARK(header_field);
+
+ parser->index = 0;
+ UPDATE_STATE(s_header_field);
+
+ switch (c) {
+ case 'c':
+ parser->header_state = h_C;
+ break;
+
+ case 'p':
+ parser->header_state = h_matching_proxy_connection;
+ break;
+
+ case 't':
+ parser->header_state = h_matching_transfer_encoding;
+ break;
+
+ case 'u':
+ parser->header_state = h_matching_upgrade;
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_field:
+ {
+ const char* start = p;
+ for (; p != data + len; p++) {
+ ch = *p;
+ c = TOKEN(ch);
+
+ if (!c)
+ break;
+
+ switch (parser->header_state) {
+ case h_general: {
+ size_t limit = data + len - p;
+ limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+ while (p+1 < data + limit && TOKEN(p[1])) {
+ p++;
+ }
+ break;
+ }
+
+ case h_C:
+ parser->index++;
+ parser->header_state = (c == 'o' ? h_CO : h_general);
+ break;
+
+ case h_CO:
+ parser->index++;
+ parser->header_state = (c == 'n' ? h_CON : h_general);
+ break;
+
+ case h_CON:
+ parser->index++;
+ switch (c) {
+ case 'n':
+ parser->header_state = h_matching_connection;
+ break;
+ case 't':
+ parser->header_state = h_matching_content_length;
+ break;
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+
+ /* connection */
+
+ case h_matching_connection:
+ parser->index++;
+ if (parser->index > sizeof(CONNECTION)-1
+ || c != CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* proxy-connection */
+
+ case h_matching_proxy_connection:
+ parser->index++;
+ if (parser->index > sizeof(PROXY_CONNECTION)-1
+ || c != PROXY_CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* content-length */
+
+ case h_matching_content_length:
+ parser->index++;
+ if (parser->index > sizeof(CONTENT_LENGTH)-1
+ || c != CONTENT_LENGTH[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+ parser->header_state = h_content_length;
+ }
+ break;
+
+ /* transfer-encoding */
+
+ case h_matching_transfer_encoding:
+ parser->index++;
+ if (parser->index > sizeof(TRANSFER_ENCODING)-1
+ || c != TRANSFER_ENCODING[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+ parser->header_state = h_transfer_encoding;
+ }
+ break;
+
+ /* upgrade */
+
+ case h_matching_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE)-1
+ || c != UPGRADE[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ parser->header_state = h_upgrade;
+ }
+ break;
+
+ case h_connection:
+ case h_content_length:
+ case h_transfer_encoding:
+ case h_upgrade:
+ if (ch != ' ') parser->header_state = h_general;
+ break;
+
+ default:
+ assert(0 && "Unknown header_state");
+ break;
+ }
+ }
+
+ if (p == data + len) {
+ --p;
+ COUNT_HEADER_SIZE(p - start);
+ break;
+ }
+
+ COUNT_HEADER_SIZE(p - start);
+
+ if (ch == ':') {
+ UPDATE_STATE(s_header_value_discard_ws);
+ CALLBACK_DATA(header_field);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ case s_header_value_discard_ws:
+ if (ch == ' ' || ch == '\t') break;
+
+ if (ch == CR) {
+ UPDATE_STATE(s_header_value_discard_ws_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_value_discard_lws);
+ break;
+ }
+
+ /* fall through */
+
+ case s_header_value_start:
+ {
+ MARK(header_value);
+
+ UPDATE_STATE(s_header_value);
+ parser->index = 0;
+
+ c = LOWER(ch);
+
+ switch (parser->header_state) {
+ case h_upgrade:
+ parser->flags |= F_UPGRADE;
+ parser->header_state = h_general;
+ break;
+
+ case h_transfer_encoding:
+ /* looking for 'Transfer-Encoding: chunked' */
+ if ('c' == c) {
+ parser->header_state = h_matching_transfer_encoding_chunked;
+ } else {
+ parser->header_state = h_general;
+ }
+ break;
+
+ case h_content_length:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ if (parser->flags & F_CONTENTLENGTH) {
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->flags |= F_CONTENTLENGTH;
+ parser->content_length = ch - '0';
+ parser->header_state = h_content_length_num;
+ break;
+
+ case h_connection:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ parser->header_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ parser->header_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ parser->header_state = h_matching_connection_upgrade;
+ } else {
+ parser->header_state = h_matching_connection_token;
+ }
+ break;
+
+ /* Multi-value `Connection` header */
+ case h_matching_connection_token_start:
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_value:
+ {
+ const char* start = p;
+ enum header_states h_state = (enum header_states) parser->header_state;
+ for (; p != data + len; p++) {
+ ch = *p;
+ if (ch == CR) {
+ UPDATE_STATE(s_header_almost_done);
+ parser->header_state = h_state;
+ CALLBACK_DATA(header_value);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_almost_done);
+ COUNT_HEADER_SIZE(p - start);
+ parser->header_state = h_state;
+ CALLBACK_DATA_NOADVANCE(header_value);
+ REEXECUTE();
+ }
+
+ if (!lenient && !IS_HEADER_CHAR(ch)) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ c = LOWER(ch);
+
+ switch (h_state) {
+ case h_general:
+ {
+ const char* p_cr;
+ const char* p_lf;
+ size_t limit = data + len - p;
+
+ limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+
+ p_cr = (const char*) memchr(p, CR, limit);
+ p_lf = (const char*) memchr(p, LF, limit);
+ if (p_cr != NULL) {
+ if (p_lf != NULL && p_cr >= p_lf)
+ p = p_lf;
+ else
+ p = p_cr;
+ } else if (UNLIKELY(p_lf != NULL)) {
+ p = p_lf;
+ } else {
+ p = data + len;
+ }
+ --p;
+ break;
+ }
+
+ case h_connection:
+ case h_transfer_encoding:
+ assert(0 && "Shouldn't get here.");
+ break;
+
+ case h_content_length:
+ if (ch == ' ') break;
+ h_state = h_content_length_num;
+ /* fall through */
+
+ case h_content_length_num:
+ {
+ uint64_t t;
+
+ if (ch == ' ') {
+ h_state = h_content_length_ws;
+ break;
+ }
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 10;
+ t += ch - '0';
+
+ /* Overflow? Test against a conservative limit for simplicity. */
+ if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ case h_content_length_ws:
+ if (ch == ' ') break;
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+
+ /* Transfer-Encoding: chunked */
+ case h_matching_transfer_encoding_chunked:
+ parser->index++;
+ if (parser->index > sizeof(CHUNKED)-1
+ || c != CHUNKED[parser->index]) {
+ h_state = h_general;
+ } else if (parser->index == sizeof(CHUNKED)-2) {
+ h_state = h_transfer_encoding_chunked;
+ }
+ break;
+
+ case h_matching_connection_token_start:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ h_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ h_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ h_state = h_matching_connection_upgrade;
+ } else if (STRICT_TOKEN(c)) {
+ h_state = h_matching_connection_token;
+ } else if (c == ' ' || c == '\t') {
+ /* Skip lws */
+ } else {
+ h_state = h_general;
+ }
+ break;
+
+ /* looking for 'Connection: keep-alive' */
+ case h_matching_connection_keep_alive:
+ parser->index++;
+ if (parser->index > sizeof(KEEP_ALIVE)-1
+ || c != KEEP_ALIVE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+ h_state = h_connection_keep_alive;
+ }
+ break;
+
+ /* looking for 'Connection: close' */
+ case h_matching_connection_close:
+ parser->index++;
+ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(CLOSE)-2) {
+ h_state = h_connection_close;
+ }
+ break;
+
+ /* looking for 'Connection: upgrade' */
+ case h_matching_connection_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE) - 1 ||
+ c != UPGRADE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ h_state = h_connection_upgrade;
+ }
+ break;
+
+ case h_matching_connection_token:
+ if (ch == ',') {
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ }
+ break;
+
+ case h_transfer_encoding_chunked:
+ if (ch != ' ') h_state = h_general;
+ break;
+
+ case h_connection_keep_alive:
+ case h_connection_close:
+ case h_connection_upgrade:
+ if (ch == ',') {
+ if (h_state == h_connection_keep_alive) {
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ } else if (h_state == h_connection_close) {
+ parser->flags |= F_CONNECTION_CLOSE;
+ } else if (h_state == h_connection_upgrade) {
+ parser->flags |= F_CONNECTION_UPGRADE;
+ }
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ } else if (ch != ' ') {
+ h_state = h_matching_connection_token;
+ }
+ break;
+
+ default:
+ UPDATE_STATE(s_header_value);
+ h_state = h_general;
+ break;
+ }
+ }
+ parser->header_state = h_state;
+
+ if (p == data + len)
+ --p;
+
+ COUNT_HEADER_SIZE(p - start);
+ break;
+ }
+
+ case s_header_almost_done:
+ {
+ if (UNLIKELY(ch != LF)) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
+ UPDATE_STATE(s_header_value_lws);
+ break;
+ }
+
+ case s_header_value_lws:
+ {
+ if (ch == ' ' || ch == '\t') {
+ UPDATE_STATE(s_header_value_start);
+ REEXECUTE();
+ }
+
+ /* finished the header */
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
+ default:
+ break;
+ }
+
+ UPDATE_STATE(s_header_field_start);
+ REEXECUTE();
+ }
+
+ case s_header_value_discard_ws_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+ UPDATE_STATE(s_header_value_discard_lws);
+ break;
+ }
+
+ case s_header_value_discard_lws:
+ {
+ if (ch == ' ' || ch == '\t') {
+ UPDATE_STATE(s_header_value_discard_ws);
+ break;
+ } else {
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ default:
+ break;
+ }
+
+ /* header value was empty */
+ MARK(header_value);
+ UPDATE_STATE(s_header_field_start);
+ CALLBACK_DATA_NOADVANCE(header_value);
+ REEXECUTE();
+ }
+ }
+
+ case s_headers_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ if (parser->flags & F_TRAILING) {
+ /* End of a chunked request */
+ UPDATE_STATE(s_message_done);
+ CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
+ REEXECUTE();
+ }
+
+ /* Cannot use chunked encoding and a content-length header together
+ per the HTTP specification. */
+ if ((parser->flags & F_CHUNKED) &&
+ (parser->flags & F_CONTENTLENGTH)) {
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+ goto error;
+ }
+
+ UPDATE_STATE(s_headers_done);
+
+ /* Set this here so that on_headers_complete() callbacks can see it */
+ if ((parser->flags & F_UPGRADE) &&
+ (parser->flags & F_CONNECTION_UPGRADE)) {
+ /* For responses, "Upgrade: foo" and "Connection: upgrade" are
+ * mandatory only when it is a 101 Switching Protocols response,
+ * otherwise it is purely informational, to announce support.
+ */
+ parser->upgrade =
+ (parser->type == HTTP_REQUEST || parser->status_code == 101);
+ } else {
+ parser->upgrade = (parser->method == HTTP_CONNECT);
+ }
+
+ /* Here we call the headers_complete callback. This is somewhat
+ * different than other callbacks because if the user returns 1, we
+ * will interpret that as saying that this message has no body. This
+ * is needed for the annoying case of recieving a response to a HEAD
+ * request.
+ *
+ * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+ * we have to simulate it by handling a change in errno below.
+ */
+ if (settings->on_headers_complete) {
+ switch (settings->on_headers_complete(parser)) {
+ case 0:
+ break;
+
+ case 2:
+ parser->upgrade = 1;
+
+ /* fall through */
+ case 1:
+ parser->flags |= F_SKIPBODY;
+ break;
+
+ default:
+ SET_ERRNO(HPE_CB_headers_complete);
+ RETURN(p - data); /* Error */
+ }
+ }
+
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ RETURN(p - data);
+ }
+
+ REEXECUTE();
+ }
+
+ case s_headers_done:
+ {
+ int hasBody;
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+ nread = 0;
+
+ hasBody = parser->flags & F_CHUNKED ||
+ (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
+ if (parser->upgrade && (parser->method == HTTP_CONNECT ||
+ (parser->flags & F_SKIPBODY) || !hasBody)) {
+ /* Exit, the rest of the message is in a different protocol. */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ RETURN((p - data) + 1);
+ }
+
+ if (parser->flags & F_SKIPBODY) {
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->flags & F_CHUNKED) {
+ /* chunked encoding - ignore Content-Length header */
+ UPDATE_STATE(s_chunk_size_start);
+ } else {
+ if (parser->content_length == 0) {
+ /* Content-Length header given but zero: Content-Length: 0\r\n */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->content_length != ULLONG_MAX) {
+ /* Content-Length header given and non-zero */
+ UPDATE_STATE(s_body_identity);
+ } else {
+ if (!http_message_needs_eof(parser)) {
+ /* Assume content-length 0 - read the next */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else {
+ /* Read body until EOF */
+ UPDATE_STATE(s_body_identity_eof);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case s_body_identity:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* The difference between advancing content_length and p is because
+ * the latter will automaticaly advance on the next loop iteration.
+ * Further, if content_length ends up at 0, we want to see the last
+ * byte again for our message complete callback.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ UPDATE_STATE(s_message_done);
+
+ /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+ *
+ * The alternative to doing this is to wait for the next byte to
+ * trigger the data callback, just as in every other case. The
+ * problem with this is that this makes it difficult for the test
+ * harness to distinguish between complete-on-EOF and
+ * complete-on-length. It's not clear that this distinction is
+ * important for applications, but let's keep it for now.
+ */
+ CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+ REEXECUTE();
+ }
+
+ break;
+ }
+
+ /* read until EOF */
+ case s_body_identity_eof:
+ MARK(body);
+ p = data + len - 1;
+
+ break;
+
+ case s_message_done:
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ if (parser->upgrade) {
+ /* Exit, the rest of the message is in a different protocol. */
+ RETURN((p - data) + 1);
+ }
+ break;
+
+ case s_chunk_size_start:
+ {
+ assert(nread == 1);
+ assert(parser->flags & F_CHUNKED);
+
+ unhex_val = unhex[(unsigned char)ch];
+ if (UNLIKELY(unhex_val == -1)) {
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ parser->content_length = unhex_val;
+ UPDATE_STATE(s_chunk_size);
+ break;
+ }
+
+ case s_chunk_size:
+ {
+ uint64_t t;
+
+ assert(parser->flags & F_CHUNKED);
+
+ if (ch == CR) {
+ UPDATE_STATE(s_chunk_size_almost_done);
+ break;
+ }
+
+ unhex_val = unhex[(unsigned char)ch];
+
+ if (unhex_val == -1) {
+ if (ch == ';' || ch == ' ') {
+ UPDATE_STATE(s_chunk_parameters);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 16;
+ t += unhex_val;
+
+ /* Overflow? Test against a conservative limit for simplicity. */
+ if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ case s_chunk_parameters:
+ {
+ assert(parser->flags & F_CHUNKED);
+ /* just ignore this shit. TODO check for overflow */
+ if (ch == CR) {
+ UPDATE_STATE(s_chunk_size_almost_done);
+ break;
+ }
+ break;
+ }
+
+ case s_chunk_size_almost_done:
+ {
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+ nread = 0;
+
+ if (parser->content_length == 0) {
+ parser->flags |= F_TRAILING;
+ UPDATE_STATE(s_header_field_start);
+ } else {
+ UPDATE_STATE(s_chunk_data);
+ }
+ CALLBACK_NOTIFY(chunk_header);
+ break;
+ }
+
+ case s_chunk_data:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* See the explanation in s_body_identity for why the content
+ * length and data pointers are managed this way.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ UPDATE_STATE(s_chunk_data_almost_done);
+ }
+
+ break;
+ }
+
+ case s_chunk_data_almost_done:
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length == 0);
+ STRICT_CHECK(ch != CR);
+ UPDATE_STATE(s_chunk_data_done);
+ CALLBACK_DATA(body);
+ break;
+
+ case s_chunk_data_done:
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+ parser->nread = 0;
+ nread = 0;
+ UPDATE_STATE(s_chunk_size_start);
+ CALLBACK_NOTIFY(chunk_complete);
+ break;
+
+ default:
+ assert(0 && "unhandled state");
+ SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+ goto error;
+ }
+ }
+
+ /* Run callbacks for any marks that we have leftover after we ran our of
+ * bytes. There should be at most one of these set, so it's OK to invoke
+ * them in series (unset marks will not result in callbacks).
+ *
+ * We use the NOADVANCE() variety of callbacks here because 'p' has already
+ * overflowed 'data' and this allows us to correct for the off-by-one that
+ * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+ * value that's in-bounds).
+ */
+
+ assert(((header_field_mark ? 1 : 0) +
+ (header_value_mark ? 1 : 0) +
+ (url_mark ? 1 : 0) +
+ (body_mark ? 1 : 0) +
+ (status_mark ? 1 : 0)) <= 1);
+
+ CALLBACK_DATA_NOADVANCE(header_field);
+ CALLBACK_DATA_NOADVANCE(header_value);
+ CALLBACK_DATA_NOADVANCE(url);
+ CALLBACK_DATA_NOADVANCE(body);
+ CALLBACK_DATA_NOADVANCE(status);
+
+ RETURN(len);
+
+error:
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+ SET_ERRNO(HPE_UNKNOWN);
+ }
+
+ RETURN(p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (const http_parser *parser)
+{
+ if (parser->type == HTTP_REQUEST) {
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 || /* Not Modified */
+ parser->flags & F_SKIPBODY) { /* response to a HEAD request */
+ return 0;
+ }
+
+ if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+http_should_keep_alive (const http_parser *parser)
+{
+ if (parser->http_major > 0 && parser->http_minor > 0) {
+ /* HTTP/1.1 */
+ if (parser->flags & F_CONNECTION_CLOSE) {
+ return 0;
+ }
+ } else {
+ /* HTTP/1.0 or earlier */
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+ return 0;
+ }
+ }
+
+ return !http_message_needs_eof(parser);
+}
+
+
+const char *
+http_method_str (enum http_method m)
+{
+ return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+const char *
+http_status_str (enum http_status s)
+{
+ switch (s) {
+#define XX(num, name, string) case HTTP_STATUS_##name: return #string;
+ HTTP_STATUS_MAP(XX)
+#undef XX
+ default: return "<unknown>";
+ }
+}
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+ void *data = parser->data; /* preserve application data */
+ memset(parser, 0, sizeof(*parser));
+ parser->data = data;
+ parser->type = t;
+ parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+ parser->http_errno = HPE_OK;
+}
+
+void
+http_parser_settings_init(http_parser_settings *settings)
+{
+ memset(settings, 0, sizeof(*settings));
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+ return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+ return http_strerror_tab[err].description;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+ switch(s) {
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ if (ch == '@') {
+ return s_http_host_start;
+ }
+
+ if (IS_USERINFO_CHAR(ch)) {
+ return s_http_userinfo;
+ }
+ break;
+
+ case s_http_host_start:
+ if (ch == '[') {
+ return s_http_host_v6_start;
+ }
+
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ break;
+
+ case s_http_host:
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ /* fall through */
+ case s_http_host_v6_end:
+ if (ch == ':') {
+ return s_http_host_port_start;
+ }
+
+ break;
+
+ case s_http_host_v6:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* fall through */
+ case s_http_host_v6_start:
+ if (IS_HEX(ch) || ch == ':' || ch == '.') {
+ return s_http_host_v6;
+ }
+
+ if (s == s_http_host_v6 && ch == '%') {
+ return s_http_host_v6_zone_start;
+ }
+ break;
+
+ case s_http_host_v6_zone:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* fall through */
+ case s_http_host_v6_zone_start:
+ /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
+ ch == '~') {
+ return s_http_host_v6_zone;
+ }
+ break;
+
+ case s_http_host_port:
+ case s_http_host_port_start:
+ if (IS_NUM(ch)) {
+ return s_http_host_port;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+ enum http_host_state s;
+
+ const char *p;
+ size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+ assert(u->field_set & (1 << UF_HOST));
+
+ u->field_data[UF_HOST].len = 0;
+
+ s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+ for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+ enum http_host_state new_s = http_parse_host_char(s, *p);
+
+ if (new_s == s_http_host_dead) {
+ return 1;
+ }
+
+ switch(new_s) {
+ case s_http_host:
+ if (s != s_http_host) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6:
+ if (s != s_http_host_v6) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_port:
+ if (s != s_http_host_port) {
+ u->field_data[UF_PORT].off = p - buf;
+ u->field_data[UF_PORT].len = 0;
+ u->field_set |= (1 << UF_PORT);
+ }
+ u->field_data[UF_PORT].len++;
+ break;
+
+ case s_http_userinfo:
+ if (s != s_http_userinfo) {
+ u->field_data[UF_USERINFO].off = p - buf ;
+ u->field_data[UF_USERINFO].len = 0;
+ u->field_set |= (1 << UF_USERINFO);
+ }
+ u->field_data[UF_USERINFO].len++;
+ break;
+
+ default:
+ break;
+ }
+ s = new_s;
+ }
+
+ /* Make sure we don't end somewhere unexpected */
+ switch (s) {
+ case s_http_host_start:
+ case s_http_host_v6_start:
+ case s_http_host_v6:
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ case s_http_host_port_start:
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void
+http_parser_url_init(struct http_parser_url *u) {
+ memset(u, 0, sizeof(*u));
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+ struct http_parser_url *u)
+{
+ enum state s;
+ const char *p;
+ enum http_parser_url_fields uf, old_uf;
+ int found_at = 0;
+
+ if (buflen == 0) {
+ return 1;
+ }
+
+ u->port = u->field_set = 0;
+ s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+ old_uf = UF_MAX;
+
+ for (p = buf; p < buf + buflen; p++) {
+ s = parse_url_char(s, *p);
+
+ /* Figure out the next field that we're operating on */
+ switch (s) {
+ case s_dead:
+ return 1;
+
+ /* Skip delimeters */
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ case s_req_query_string_start:
+ case s_req_fragment_start:
+ continue;
+
+ case s_req_schema:
+ uf = UF_SCHEMA;
+ break;
+
+ case s_req_server_with_at:
+ found_at = 1;
+
+ /* fall through */
+ case s_req_server:
+ uf = UF_HOST;
+ break;
+
+ case s_req_path:
+ uf = UF_PATH;
+ break;
+
+ case s_req_query_string:
+ uf = UF_QUERY;
+ break;
+
+ case s_req_fragment:
+ uf = UF_FRAGMENT;
+ break;
+
+ default:
+ assert(!"Unexpected state");
+ return 1;
+ }
+
+ /* Nothing's changed; soldier on */
+ if (uf == old_uf) {
+ u->field_data[uf].len++;
+ continue;
+ }
+
+ u->field_data[uf].off = p - buf;
+ u->field_data[uf].len = 1;
+
+ u->field_set |= (1 << uf);
+ old_uf = uf;
+ }
+
+ /* host must be present if there is a schema */
+ /* parsing http:///toto will fail */
+ if ((u->field_set & (1 << UF_SCHEMA)) &&
+ (u->field_set & (1 << UF_HOST)) == 0) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_HOST)) {
+ if (http_parse_host(buf, u, found_at) != 0) {
+ return 1;
+ }
+ }
+
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_PORT)) {
+ uint16_t off;
+ uint16_t len;
+ const char* p;
+ const char* end;
+ unsigned long v;
+
+ off = u->field_data[UF_PORT].off;
+ len = u->field_data[UF_PORT].len;
+ end = buf + off + len;
+
+ /* NOTE: The characters are already validated and are in the [0-9] range */
+ assert(off + len <= buflen && "Port number overflow");
+ v = 0;
+ for (p = buf + off; p < end; p++) {
+ v *= 10;
+ v += *p - '0';
+
+ /* Ports have a max value of 2^16 */
+ if (v > 0xffff) {
+ return 1;
+ }
+ }
+
+ u->port = (uint16_t) v;
+ }
+
+ return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+ /* Users should only be pausing/unpausing a parser that is not in an error
+ * state. In non-debug builds, there's not much that we can do about this
+ * other than ignore it.
+ */
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+ HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+ uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
+ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+ } else {
+ assert(0 && "Attempting to pause parser in error state");
+ }
+}
+
+int
+http_body_is_final(const struct http_parser *parser) {
+ return parser->state == s_message_done;
+}
+
+unsigned long
+http_parser_version(void) {
+ return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
+ HTTP_PARSER_VERSION_MINOR * 0x00100 |
+ HTTP_PARSER_VERSION_PATCH * 0x00001;
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
new file mode 100644
index 0000000..c14b3f4
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
@@ -0,0 +1,54 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include <jni.h>
+
+#include "edu_wpi_first_wpiutil_WPIUtilJNI.h"
+#include "wpi/PortForwarder.h"
+#include "wpi/jni_util.h"
+
+using namespace wpi::java;
+
+extern "C" {
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
+ return JNI_ERR;
+
+ return JNI_VERSION_1_6;
+}
+
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {}
+
+/*
+ * Class: edu_wpi_first_wpiutil_WPIUtilJNI
+ * Method: addPortForwarder
+ * Signature: (ILjava/lang/String;I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_wpiutil_WPIUtilJNI_addPortForwarder
+ (JNIEnv* env, jclass, jint port, jstring remoteHost, jint remotePort)
+{
+ wpi::PortForwarder::GetInstance().Add(static_cast<unsigned int>(port),
+ JStringRef{env, remoteHost}.str(),
+ static_cast<unsigned int>(remotePort));
+}
+
+/*
+ * Class: edu_wpi_first_wpiutil_WPIUtilJNI
+ * Method: removePortForwarder
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_wpiutil_WPIUtilJNI_removePortForwarder
+ (JNIEnv* env, jclass, jint port)
+{
+ wpi::PortForwarder::GetInstance().Remove(port);
+}
+
+} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/json.cpp b/wpiutil/src/main/native/cpp/json.cpp
new file mode 100644
index 0000000..ba9cd8e
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json.cpp
@@ -0,0 +1,1493 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.1.2
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#define WPI_JSON_IMPLEMENTATION
+#include "wpi/json.h"
+
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+namespace detail {
+
+exception::exception(int id_, const Twine& what_arg)
+ : id(id_), m(what_arg.str()) {}
+
+parse_error parse_error::create(int id_, std::size_t byte_, const Twine& what_arg)
+{
+ return parse_error(id_, byte_, "[json.exception.parse_error." + Twine(id_) + "] parse error" +
+ (byte_ != 0 ? (" at " + Twine(byte_)) : Twine("")) +
+ ": " + what_arg);
+}
+
+invalid_iterator invalid_iterator::create(int id_, const Twine& what_arg)
+{
+ return invalid_iterator(id_, "[json.exception.invalid_iterator." + Twine(id_) + "] " + what_arg);
+}
+
+type_error type_error::create(int id_, const Twine& what_arg)
+{
+ return type_error(id_, "[json.exception.type_error." + Twine(id_) + "] " + what_arg);
+}
+
+out_of_range out_of_range::create(int id_, const Twine& what_arg)
+{
+ return out_of_range(id_, "[json.exception.out_of_range." + Twine(id_) + "] " + what_arg);
+}
+
+other_error other_error::create(int id_, const Twine& what_arg)
+{
+ return other_error(id_, "[json.exception.other_error." + Twine(id_) + "] " + what_arg);
+}
+
+} // namespace detail
+
+json::json_value::json_value(value_t t)
+{
+ switch (t)
+ {
+ case value_t::object:
+ {
+ object = create<object_t>();
+ break;
+ }
+
+ case value_t::array:
+ {
+ array = create<array_t>();
+ break;
+ }
+
+ case value_t::string:
+ {
+ string = create<std::string>("");
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ boolean = false;
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ number_integer = 0;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ number_unsigned = 0u;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ number_float = 0.0;
+ break;
+ }
+
+ case value_t::null:
+ {
+ object = nullptr; // silence warning, see #821
+ break;
+ }
+
+ default:
+ {
+ object = nullptr; // silence warning, see #821
+ if (JSON_UNLIKELY(t == value_t::null))
+ {
+ JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.2")); // LCOV_EXCL_LINE
+ }
+ break;
+ }
+ }
+}
+
+void json::json_value::destroy(value_t t) noexcept
+{
+ switch (t)
+ {
+ case value_t::object:
+ {
+ std::allocator<object_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::allocator<array_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
+ break;
+ }
+
+ case value_t::string:
+ {
+ std::allocator<std::string> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+json::json(initializer_list_t init,
+ bool type_deduction,
+ value_t manual_type)
+{
+ // check if each element is an array with two elements whose first
+ // element is a string
+ bool is_an_object = std::all_of(init.begin(), init.end(),
+ [](const detail::json_ref<json>& element_ref)
+ {
+ return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());
+ });
+
+ // adjust type if type deduction is not wanted
+ if (not type_deduction)
+ {
+ // if array is wanted, do not create an object though possible
+ if (manual_type == value_t::array)
+ {
+ is_an_object = false;
+ }
+
+ // if object is wanted but impossible, throw an exception
+ if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))
+ {
+ JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
+ }
+ }
+
+ if (is_an_object)
+ {
+ // the initializer list is a list of pairs -> create object
+ m_type = value_t::object;
+ m_value = value_t::object;
+
+ std::for_each(init.begin(), init.end(), [this](const detail::json_ref<json>& element_ref)
+ {
+ auto element = element_ref.moved_or_copied();
+ m_value.object->try_emplace(
+ *((*element.m_value.array)[0].m_value.string),
+ std::move((*element.m_value.array)[1]));
+ });
+ }
+ else
+ {
+ // the initializer list describes an array -> create array
+ m_type = value_t::array;
+ m_value.array = create<array_t>(init.begin(), init.end());
+ }
+
+ assert_invariant();
+}
+
+json::json(size_type cnt, const json& val)
+ : m_type(value_t::array)
+{
+ m_value.array = create<array_t>(cnt, val);
+ assert_invariant();
+}
+
+json::json(const json& other)
+ : m_type(other.m_type)
+{
+ // check of passed value is valid
+ other.assert_invariant();
+
+ switch (m_type)
+ {
+ case value_t::object:
+ {
+ m_value = *other.m_value.object;
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_value = *other.m_value.array;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_value = *other.m_value.string;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_value = other.m_value.boolean;
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ m_value = other.m_value.number_integer;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_value = other.m_value.number_unsigned;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_value = other.m_value.number_float;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ assert_invariant();
+}
+
+json json::meta()
+{
+ json result;
+
+ result["copyright"] = "(C) 2013-2017 Niels Lohmann, (C) 2017-2018 FIRST";
+ result["name"] = "WPI version of JSON for Modern C++";
+ result["url"] = "https://github.com/wpilibsuite/allwpilib";
+ result["version"]["string"] =
+ std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
+ std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
+ std::to_string(NLOHMANN_JSON_VERSION_PATCH);
+ result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
+ result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
+ result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
+
+#ifdef _WIN32
+ result["platform"] = "win32";
+#elif defined __linux__
+ result["platform"] = "linux";
+#elif defined __APPLE__
+ result["platform"] = "apple";
+#elif defined __unix__
+ result["platform"] = "unix";
+#else
+ result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+ result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+ result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
+#elif defined(__HP_cc) || defined(__HP_aCC)
+ result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+ result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+ result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+ result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+ result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+ result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+#ifdef __cplusplus
+ result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+ result["compiler"]["c++"] = "unknown";
+#endif
+ return result;
+}
+
+json::reference json::at(size_type idx)
+{
+ // at only works for arrays
+ if (JSON_LIKELY(is_array()))
+ {
+ JSON_TRY
+ {
+ return m_value.array->at(idx);
+ }
+ JSON_CATCH (std::out_of_range&)
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ }
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+ }
+}
+
+json::const_reference json::at(size_type idx) const
+{
+ // at only works for arrays
+ if (JSON_LIKELY(is_array()))
+ {
+ JSON_TRY
+ {
+ return m_value.array->at(idx);
+ }
+ JSON_CATCH (std::out_of_range&)
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ }
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+ }
+}
+
+json::reference json::at(StringRef key)
+{
+ // at only works for objects
+ if (JSON_LIKELY(is_object()))
+ {
+ auto it = m_value.object->find(key);
+ if (it == m_value.object->end())
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(403, "key '" + Twine(key) + "' not found"));
+ }
+ return it->second;
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+ }
+}
+
+json::const_reference json::at(StringRef key) const
+{
+ // at only works for objects
+ if (JSON_LIKELY(is_object()))
+ {
+ auto it = m_value.object->find(key);
+ if (it == m_value.object->end())
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(403, "key '" + Twine(key) + "' not found"));
+ }
+ return it->second;
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, "cannot use at() with " + Twine(type_name())));
+ }
+}
+
+json::reference json::operator[](size_type idx)
+{
+ // implicitly convert null value to an empty array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value.array = create<array_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for arrays
+ if (JSON_LIKELY(is_array()))
+ {
+ // fill up array with null values if given idx is outside range
+ if (idx >= m_value.array->size())
+ {
+ m_value.array->insert(m_value.array->end(),
+ idx - m_value.array->size() + 1,
+ json());
+ }
+
+ return m_value.array->operator[](idx);
+ }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::const_reference json::operator[](size_type idx) const
+{
+ // const operator[] only works for arrays
+ if (JSON_LIKELY(is_array()))
+ {
+ return m_value.array->operator[](idx);
+ }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::reference json::operator[](StringRef key)
+{
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for objects
+ if (JSON_LIKELY(is_object()))
+ {
+ return m_value.object->operator[](key);
+ }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::const_reference json::operator[](StringRef key) const
+{
+ // const operator[] only works for objects
+ if (JSON_LIKELY(is_object()))
+ {
+ assert(m_value.object->find(key) != m_value.object->end());
+ return m_value.object->find(key)->second;
+ }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + Twine(type_name())));
+}
+
+json::size_type json::erase(StringRef key)
+{
+ // this erase only works for objects
+ if (JSON_LIKELY(is_object()))
+ {
+ return m_value.object->erase(key);
+ }
+
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + Twine(type_name())));
+}
+
+void json::erase(const size_type idx)
+{
+ // this erase only works for arrays
+ if (JSON_LIKELY(is_array()))
+ {
+ if (JSON_UNLIKELY(idx >= size()))
+ {
+ JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ }
+
+ m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
+ }
+ else
+ {
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + Twine(type_name())));
+ }
+}
+
+json::iterator json::find(StringRef key)
+{
+ auto result = end();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_value.object->find(key);
+ }
+
+ return result;
+}
+
+json::const_iterator json::find(StringRef key) const
+{
+ auto result = cend();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_value.object->find(key);
+ }
+
+ return result;
+}
+
+json::size_type json::count(StringRef key) const
+{
+ // return 0 for all nonobject types
+ return is_object() ? m_value.object->count(key) : 0;
+}
+
+bool json::empty() const noexcept
+{
+ switch (m_type)
+ {
+ case value_t::null:
+ {
+ // null values are empty
+ return true;
+ }
+
+ case value_t::array:
+ {
+ // delegate call to array_t::empty()
+ return m_value.array->empty();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::empty()
+ return m_value.object->empty();
+ }
+
+ default:
+ {
+ // all other types are nonempty
+ return false;
+ }
+ }
+}
+
+json::size_type json::size() const noexcept
+{
+ switch (m_type)
+ {
+ case value_t::null:
+ {
+ // null values are empty
+ return 0;
+ }
+
+ case value_t::array:
+ {
+ // delegate call to array_t::size()
+ return m_value.array->size();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::size()
+ return m_value.object->size();
+ }
+
+ default:
+ {
+ // all other types have size 1
+ return 1;
+ }
+ }
+}
+
+json::size_type json::max_size() const noexcept
+{
+ switch (m_type)
+ {
+ case value_t::array:
+ {
+ // delegate call to array_t::max_size()
+ return m_value.array->max_size();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to std::allocator<object_t>::max_size()
+ return std::allocator_traits<object_t>::max_size(*m_value.object);
+ }
+
+ default:
+ {
+ // all other types have max_size() == size()
+ return size();
+ }
+ }
+}
+
+void json::clear() noexcept
+{
+ switch (m_type)
+ {
+ case value_t::number_integer:
+ {
+ m_value.number_integer = 0;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_value.number_unsigned = 0;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_value.number_float = 0.0;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_value.boolean = false;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_value.string->clear();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_value.array->clear();
+ break;
+ }
+
+ case value_t::object:
+ {
+ m_value.object->clear();
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void json::push_back(json&& val)
+{
+ // push_back only works for null objects or arrays
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
+ {
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + Twine(type_name())));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array (move semantics)
+ m_value.array->push_back(std::move(val));
+ // invalidate object
+ val.m_type = value_t::null;
+}
+
+void json::push_back(const json& val)
+{
+ // push_back only works for null objects or arrays
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
+ {
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + Twine(type_name())));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array
+ m_value.array->push_back(val);
+}
+
+void json::push_back(initializer_list_t init)
+{
+ if (is_object() and init.size() == 2 and (*init.begin())->is_string())
+ {
+ std::string key = init.begin()->moved_or_copied();
+ push_back(std::pair<StringRef, json>(key, (init.begin() + 1)->moved_or_copied()));
+ }
+ else
+ {
+ push_back(json(init));
+ }
+}
+
+json::iterator json::insert(const_iterator pos, const json& val)
+{
+ // insert only works for arrays
+ if (JSON_LIKELY(is_array()))
+ {
+ // check if iterator pos fits to this JSON value
+ if (JSON_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ }
+
+ // insert to array and return iterator
+ iterator result(this);
+ result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
+ return result;
+ }
+
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+}
+
+json::iterator json::insert(const_iterator pos, size_type cnt, const json& val)
+{
+ // insert only works for arrays
+ if (JSON_LIKELY(is_array()))
+ {
+ // check if iterator pos fits to this JSON value
+ if (JSON_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ }
+
+ // insert to array and return iterator
+ iterator result(this);
+ result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
+ return result;
+ }
+
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+}
+
+json::iterator json::insert(const_iterator pos, const_iterator first, const_iterator last)
+{
+ // insert only works for arrays
+ if (JSON_UNLIKELY(not is_array()))
+ {
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ }
+
+ // check if iterator pos fits to this JSON value
+ if (JSON_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ }
+
+ if (JSON_UNLIKELY(first.m_object == this))
+ {
+ JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
+ }
+
+ // insert to array and return iterator
+ iterator result(this);
+ result.m_it.array_iterator = m_value.array->insert(
+ pos.m_it.array_iterator,
+ first.m_it.array_iterator,
+ last.m_it.array_iterator);
+ return result;
+}
+
+json::iterator json::insert(const_iterator pos, initializer_list_t ilist)
+{
+ // insert only works for arrays
+ if (JSON_UNLIKELY(not is_array()))
+ {
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ }
+
+ // check if iterator pos fits to this JSON value
+ if (JSON_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ }
+
+ // insert to array and return iterator
+ iterator result(this);
+ result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end());
+ return result;
+}
+
+void json::insert(const_iterator first, const_iterator last)
+{
+ // insert only works for objects
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + Twine(type_name())));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_UNLIKELY(not first.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ }
+
+ for (auto it = first.m_it.object_iterator, end = last.m_it.object_iterator; it != end; ++it)
+ {
+ m_value.object->insert(std::make_pair(it->first(), it->second));
+ }
+}
+
+void json::update(const_reference j)
+{
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(type_name())));
+ }
+ if (JSON_UNLIKELY(not j.is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(j.type_name())));
+ }
+
+ for (auto it = j.cbegin(); it != j.cend(); ++it)
+ {
+ m_value.object->operator[](it.key()) = it.value();
+ }
+}
+
+void json::update(const_iterator first, const_iterator last)
+{
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + Twine(type_name())));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_UNLIKELY(not first.m_object->is_object()
+ or not last.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ }
+
+ for (auto it = first; it != last; ++it)
+ {
+ m_value.object->operator[](it.key()) = it.value();
+ }
+}
+
+bool operator==(json::const_reference lhs, json::const_reference rhs) noexcept
+{
+ const auto lhs_type = lhs.type();
+ const auto rhs_type = rhs.type();
+
+ if (lhs_type == rhs_type)
+ {
+ switch (lhs_type)
+ {
+ case json::value_t::array:
+ return (*lhs.m_value.array == *rhs.m_value.array);
+
+ case json::value_t::object:
+ return (*lhs.m_value.object == *rhs.m_value.object);
+
+ case json::value_t::null:
+ return true;
+
+ case json::value_t::string:
+ return (*lhs.m_value.string == *rhs.m_value.string);
+
+ case json::value_t::boolean:
+ return (lhs.m_value.boolean == rhs.m_value.boolean);
+
+ case json::value_t::number_integer:
+ return (lhs.m_value.number_integer == rhs.m_value.number_integer);
+
+ case json::value_t::number_unsigned:
+ return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);
+
+ case json::value_t::number_float:
+ return (lhs.m_value.number_float == rhs.m_value.number_float);
+
+ default:
+ return false;
+ }
+ }
+ else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
+ {
+ return (static_cast<double>(lhs.m_value.number_integer) == rhs.m_value.number_float);
+ }
+ else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
+ {
+ return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_integer));
+ }
+ else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
+ {
+ return (static_cast<double>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);
+ }
+ else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
+ {
+ return (lhs.m_value.number_float == static_cast<double>(rhs.m_value.number_unsigned));
+ }
+ else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
+ {
+ return (static_cast<int64_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);
+ }
+ else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
+ {
+ return (lhs.m_value.number_integer == static_cast<int64_t>(rhs.m_value.number_unsigned));
+ }
+
+ return false;
+}
+
+bool operator<(json::const_reference lhs, json::const_reference rhs) noexcept
+{
+ const auto lhs_type = lhs.type();
+ const auto rhs_type = rhs.type();
+
+ if (lhs_type == rhs_type)
+ {
+ switch (lhs_type)
+ {
+ case json::value_t::array:
+ return (*lhs.m_value.array) < (*rhs.m_value.array);
+
+ case json::value_t::object:
+ return *lhs.m_value.object < *rhs.m_value.object;
+
+ case json::value_t::null:
+ return false;
+
+ case json::value_t::string:
+ return *lhs.m_value.string < *rhs.m_value.string;
+
+ case json::value_t::boolean:
+ return lhs.m_value.boolean < rhs.m_value.boolean;
+
+ case json::value_t::number_integer:
+ return lhs.m_value.number_integer < rhs.m_value.number_integer;
+
+ case json::value_t::number_unsigned:
+ return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;
+
+ case json::value_t::number_float:
+ return lhs.m_value.number_float < rhs.m_value.number_float;
+
+ default:
+ return false;
+ }
+ }
+ else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_float)
+ {
+ return static_cast<double>(lhs.m_value.number_integer) < rhs.m_value.number_float;
+ }
+ else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_integer)
+ {
+ return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_integer);
+ }
+ else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_float)
+ {
+ return static_cast<double>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
+ }
+ else if (lhs_type == json::value_t::number_float and rhs_type == json::value_t::number_unsigned)
+ {
+ return lhs.m_value.number_float < static_cast<double>(rhs.m_value.number_unsigned);
+ }
+ else if (lhs_type == json::value_t::number_integer and rhs_type == json::value_t::number_unsigned)
+ {
+ return lhs.m_value.number_integer < static_cast<int64_t>(rhs.m_value.number_unsigned);
+ }
+ else if (lhs_type == json::value_t::number_unsigned and rhs_type == json::value_t::number_integer)
+ {
+ return static_cast<int64_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
+ }
+
+ // We only reach this line if we cannot compare values. In that case,
+ // we compare types. Note we have to call the operator explicitly,
+ // because MSVC has problems otherwise.
+ return operator<(lhs_type, rhs_type);
+}
+
+const char* json::type_name() const noexcept
+{
+ {
+ switch (m_type)
+ {
+ case value_t::null:
+ return "null";
+ case value_t::object:
+ return "object";
+ case value_t::array:
+ return "array";
+ case value_t::string:
+ return "string";
+ case value_t::boolean:
+ return "boolean";
+ case value_t::discarded:
+ return "discarded";
+ default:
+ return "number";
+ }
+ }
+}
+
+json json::patch(const json& json_patch) const
+{
+ // make a working copy to apply the patch to
+ json result = *this;
+
+ // the valid JSON Patch operations
+ enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+ const auto get_op = [](const std::string & op)
+ {
+ if (op == "add")
+ {
+ return patch_operations::add;
+ }
+ if (op == "remove")
+ {
+ return patch_operations::remove;
+ }
+ if (op == "replace")
+ {
+ return patch_operations::replace;
+ }
+ if (op == "move")
+ {
+ return patch_operations::move;
+ }
+ if (op == "copy")
+ {
+ return patch_operations::copy;
+ }
+ if (op == "test")
+ {
+ return patch_operations::test;
+ }
+
+ return patch_operations::invalid;
+ };
+
+ // wrapper for "add" operation; add value at ptr
+ const auto operation_add = [&result](json_pointer & ptr, json val)
+ {
+ // adding to the root of the target document means replacing it
+ if (ptr.is_root())
+ {
+ result = val;
+ }
+ else
+ {
+ // make sure the top element of the pointer exists
+ json_pointer top_pointer = ptr.top();
+ if (top_pointer != ptr)
+ {
+ result.at(top_pointer);
+ }
+
+ // get reference to parent of JSON pointer ptr
+ const auto last_path = ptr.pop_back();
+ json& parent = result[ptr];
+
+ switch (parent.m_type)
+ {
+ case value_t::null:
+ case value_t::object:
+ {
+ // use operator[] to add value
+ parent[last_path] = val;
+ break;
+ }
+
+ case value_t::array:
+ {
+ if (last_path == "-")
+ {
+ // special case: append to back
+ parent.push_back(val);
+ }
+ else
+ {
+ const auto idx = json_pointer::array_index(last_path);
+ if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
+ {
+ // avoid undefined behavior
+ JSON_THROW(out_of_range::create(401, "array index " + Twine(idx) + " is out of range"));
+ }
+ else
+ {
+ // default case: insert add offset
+ parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ // if there exists a parent it cannot be primitive
+ assert(false); // LCOV_EXCL_LINE
+ }
+ }
+ }
+ };
+
+ // wrapper for "remove" operation; remove value at ptr
+ const auto operation_remove = [&result](json_pointer & ptr)
+ {
+ // get reference to parent of JSON pointer ptr
+ const auto last_path = ptr.pop_back();
+ json& parent = result.at(ptr);
+
+ // remove child
+ if (parent.is_object())
+ {
+ // perform range check
+ auto it = parent.find(last_path);
+ if (JSON_LIKELY(it != parent.end()))
+ {
+ parent.erase(it);
+ }
+ else
+ {
+ JSON_THROW(out_of_range::create(403, "key '" + Twine(last_path) + "' not found"));
+ }
+ }
+ else if (parent.is_array())
+ {
+ // note erase performs range check
+ parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
+ }
+ };
+
+ // type check: top level value must be an array
+ if (JSON_UNLIKELY(not json_patch.is_array()))
+ {
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
+ }
+
+ // iterate and apply the operations
+ for (const auto& val : json_patch)
+ {
+ // wrapper to get a value for an operation
+ const auto get_value = [&val](const std::string & op,
+ const std::string & member,
+ bool string_type) -> json &
+ {
+ // find value
+ auto it = val.m_value.object->find(member);
+
+ // context-sensitive error message
+ const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
+
+ // check if desired value is present
+ if (JSON_UNLIKELY(it == val.m_value.object->end()))
+ {
+ JSON_THROW(parse_error::create(105, 0, Twine(error_msg) + " must have member '" + Twine(member) + "'"));
+ }
+
+ // check if result is of type string
+ if (JSON_UNLIKELY(string_type and not it->second.is_string()))
+ {
+ JSON_THROW(parse_error::create(105, 0, Twine(error_msg) + " must have string member '" + Twine(member) + "'"));
+ }
+
+ // no error: return value
+ return it->second;
+ };
+
+ // type check: every element of the array must be an object
+ if (JSON_UNLIKELY(not val.is_object()))
+ {
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
+ }
+
+ // collect mandatory members
+ const std::string op = get_value("op", "op", true);
+ const std::string path = get_value(op, "path", true);
+ json_pointer ptr(path);
+
+ switch (get_op(op))
+ {
+ case patch_operations::add:
+ {
+ operation_add(ptr, get_value("add", "value", false));
+ break;
+ }
+
+ case patch_operations::remove:
+ {
+ operation_remove(ptr);
+ break;
+ }
+
+ case patch_operations::replace:
+ {
+ // the "path" location must exist - use at()
+ result.at(ptr) = get_value("replace", "value", false);
+ break;
+ }
+
+ case patch_operations::move:
+ {
+ const std::string from_path = get_value("move", "from", true);
+ json_pointer from_ptr(from_path);
+
+ // the "from" location must exist - use at()
+ json v = result.at(from_ptr);
+
+ // The move operation is functionally identical to a
+ // "remove" operation on the "from" location, followed
+ // immediately by an "add" operation at the target
+ // location with the value that was just removed.
+ operation_remove(from_ptr);
+ operation_add(ptr, v);
+ break;
+ }
+
+ case patch_operations::copy:
+ {
+ const std::string from_path = get_value("copy", "from", true);
+ const json_pointer from_ptr(from_path);
+
+ // the "from" location must exist - use at()
+ json v = result.at(from_ptr);
+
+ // The copy is functionally identical to an "add"
+ // operation at the target location using the value
+ // specified in the "from" member.
+ operation_add(ptr, v);
+ break;
+ }
+
+ case patch_operations::test:
+ {
+ bool success = false;
+ JSON_TRY
+ {
+ // check if "value" matches the one at "path"
+ // the "path" location must exist - use at()
+ success = (result.at(ptr) == get_value("test", "value", false));
+ }
+ JSON_CATCH (out_of_range&)
+ {
+ // ignore out of range errors: success remains false
+ }
+
+ // throw an exception if test fails
+ if (JSON_UNLIKELY(not success))
+ {
+ JSON_THROW(other_error::create(501, "unsuccessful: " + Twine(val.dump())));
+ }
+
+ break;
+ }
+
+ case patch_operations::invalid:
+ {
+ // op must be "add", "remove", "replace", "move", "copy", or
+ // "test"
+ JSON_THROW(parse_error::create(105, 0, "operation value '" + Twine(op) + "' is invalid"));
+ }
+ }
+ }
+
+ return result;
+}
+
+json json::diff(const json& source, const json& target,
+ const std::string& path)
+{
+ // the patch
+ json result(value_t::array);
+
+ // if the values are the same, return empty patch
+ if (source == target)
+ {
+ return result;
+ }
+
+ if (source.type() != target.type())
+ {
+ // different types: replace value
+ result.push_back(
+ {
+ {"op", "replace"}, {"path", path}, {"value", target}
+ });
+ }
+ else
+ {
+ switch (source.type())
+ {
+ case value_t::array:
+ {
+ // first pass: traverse common elements
+ std::size_t i = 0;
+ while (i < source.size() and i < target.size())
+ {
+ // recursive call to compare array values at index i
+ auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
+ result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+ ++i;
+ }
+
+ // i now reached the end of at least one array
+ // in a second pass, traverse the remaining elements
+
+ // remove my remaining elements
+ const auto end_index = static_cast<difference_type>(result.size());
+ while (i < source.size())
+ {
+ // add operations in reverse order to avoid invalid
+ // indices
+ result.insert(result.begin() + end_index, object(
+ {
+ {"op", "remove"},
+ {"path", path + "/" + std::to_string(i)}
+ }));
+ ++i;
+ }
+
+ // add other remaining elements
+ while (i < target.size())
+ {
+ result.push_back(
+ {
+ {"op", "add"},
+ {"path", path + "/" + std::to_string(i)},
+ {"value", target[i]}
+ });
+ ++i;
+ }
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ // first pass: traverse this object's elements
+ for (auto it = source.cbegin(); it != source.cend(); ++it)
+ {
+ // escape the key name to be used in a JSON patch
+ const auto key = json_pointer::escape(it.key());
+
+ if (target.find(it.key()) != target.end())
+ {
+ // recursive call to compare object values at key it
+ auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
+ result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+ }
+ else
+ {
+ // found a key that is not in o -> remove it
+ result.push_back(object(
+ {
+ {"op", "remove"}, {"path", path + "/" + key}
+ }));
+ }
+ }
+
+ // second pass: traverse other object's elements
+ for (auto it = target.cbegin(); it != target.cend(); ++it)
+ {
+ if (source.find(it.key()) == source.end())
+ {
+ // found a key that is not in this -> add it
+ const auto key = json_pointer::escape(it.key());
+ result.push_back(
+ {
+ {"op", "add"}, {"path", path + "/" + key},
+ {"value", it.value()}
+ });
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ // both primitive type: replace value
+ result.push_back(
+ {
+ {"op", "replace"}, {"path", path}, {"value", target}
+ });
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+void json::merge_patch(const json& patch)
+{
+ if (patch.is_object())
+ {
+ if (not is_object())
+ {
+ *this = object();
+ }
+ for (auto it = patch.begin(); it != patch.end(); ++it)
+ {
+ if (it.value().is_null())
+ {
+ erase(it.key());
+ }
+ else
+ {
+ operator[](it.key()).merge_patch(it.value());
+ }
+ }
+ }
+ else
+ {
+ *this = patch;
+ }
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_binary_reader.cpp b/wpiutil/src/main/native/cpp/json_binary_reader.cpp
new file mode 100644
index 0000000..9298e92
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json_binary_reader.cpp
@@ -0,0 +1,1413 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.1.2
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#define WPI_JSON_IMPLEMENTATION
+#include "wpi/json.h"
+
+#include <cmath> // ldexp
+
+#include "wpi/raw_istream.h"
+
+namespace wpi {
+
+/*!
+@brief deserialization of CBOR and MessagePack values
+*/
+class json::binary_reader
+{
+ public:
+ /*!
+ @brief create a binary reader
+
+ @param[in] adapter input adapter to read from
+ */
+ explicit binary_reader(raw_istream& s) : is(s)
+ {
+ }
+
+ /*!
+ @brief create a JSON value from CBOR input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from CBOR input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ json parse_cbor(const bool strict)
+ {
+ const auto res = parse_cbor_internal();
+ if (strict)
+ {
+ get();
+ expect_eof();
+ }
+ return res;
+ }
+
+ /*!
+ @brief create a JSON value from MessagePack input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from MessagePack input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ json parse_msgpack(const bool strict)
+ {
+ const auto res = parse_msgpack_internal();
+ if (strict)
+ {
+ get();
+ expect_eof();
+ }
+ return res;
+ }
+
+ /*!
+ @brief create a JSON value from UBJSON input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from UBJSON input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ json parse_ubjson(const bool strict)
+ {
+ const auto res = parse_ubjson_internal();
+ if (strict)
+ {
+ get_ignore_noop();
+ expect_eof();
+ }
+ return res;
+ }
+
+ /*!
+ @brief determine system byte order
+
+ @return true if and only if system's byte order is little endian
+
+ @note from http://stackoverflow.com/a/1001328/266378
+ */
+ static bool little_endianess(int num = 1) noexcept
+ {
+ return (*reinterpret_cast<char*>(&num) == 1);
+ }
+
+ private:
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+ */
+ json parse_cbor_internal(const bool get_char = true);
+
+ json parse_msgpack_internal();
+
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+ */
+ json parse_ubjson_internal(const bool get_char = true)
+ {
+ return get_ubjson_value(get_char ? get_ignore_noop() : current);
+ }
+
+ /*!
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a -'ve valued
+ `std::char_traits<char>::eof()` in that case.
+
+ @return character read from the input
+ */
+ int get()
+ {
+ ++chars_read;
+ unsigned char c;
+ is.read(c);
+ if (is.has_error())
+ {
+ current = std::char_traits<char>::eof();
+ }
+ else
+ {
+ current = c;
+ }
+ return current;
+ }
+
+ /*!
+ @return character read from the input after ignoring all 'N' entries
+ */
+ int get_ignore_noop()
+ {
+ do
+ {
+ get();
+ }
+ while (current == 'N');
+
+ return current;
+ }
+
+ /*
+ @brief read a number from the input
+
+ @tparam NumberType the type of the number
+
+ @return number of type @a NumberType
+
+ @note This function needs to respect the system's endianess, because
+ bytes in CBOR and MessagePack are stored in network order (big
+ endian) and therefore need reordering on little endian systems.
+
+ @throw parse_error.110 if input has less than `sizeof(NumberType)` bytes
+ */
+ template<typename NumberType> NumberType get_number()
+ {
+ // step 1: read input into array with system's byte order
+ std::array<uint8_t, sizeof(NumberType)> vec;
+ for (std::size_t i = 0; i < sizeof(NumberType); ++i)
+ {
+ get();
+ unexpect_eof();
+
+ // reverse byte order prior to conversion if necessary
+ if (is_little_endian)
+ {
+ vec[sizeof(NumberType) - i - 1] = static_cast<uint8_t>(current);
+ }
+ else
+ {
+ vec[i] = static_cast<uint8_t>(current); // LCOV_EXCL_LINE
+ }
+ }
+
+ // step 2: convert array into number of type T and return
+ NumberType result;
+ std::memcpy(&result, vec.data(), sizeof(NumberType));
+ return result;
+ }
+
+ /*!
+ @brief create a string by reading characters from the input
+
+ @param[in] len number of bytes to read
+
+ @note We can not reserve @a len bytes for the result, because @a len
+ may be too large. Usually, @ref unexpect_eof() detects the end of
+ the input before we run out of string memory.
+
+ @return string created by reading @a len bytes
+
+ @throw parse_error.110 if input has less than @a len bytes
+ */
+ template<typename NumberType>
+ std::string get_string(const NumberType len)
+ {
+ std::string result;
+ std::generate_n(std::back_inserter(result), len, [this]()
+ {
+ get();
+ unexpect_eof();
+ return static_cast<char>(current);
+ });
+ return result;
+ }
+
+ /*!
+ @brief reads a CBOR string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+ Additionally, CBOR's strings with indefinite lengths are supported.
+
+ @return string
+
+ @throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ std::string get_cbor_string();
+
+ template<typename NumberType>
+ json get_cbor_array(const NumberType len)
+ {
+ json result = value_t::array;
+ std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
+ {
+ return parse_cbor_internal();
+ });
+ return result;
+ }
+
+ template<typename NumberType>
+ json get_cbor_object(const NumberType len)
+ {
+ json result = value_t::object;
+ for (NumberType i = 0; i < len; ++i)
+ {
+ get();
+ auto key = get_cbor_string();
+ (*result.m_value.object)[key] = parse_cbor_internal();
+ }
+ return result;
+ }
+
+ /*!
+ @brief reads a MessagePack string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+
+ @return string
+
+ @throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ std::string get_msgpack_string();
+
+ template<typename NumberType>
+ json get_msgpack_array(const NumberType len)
+ {
+ json result = value_t::array;
+ std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
+ {
+ return parse_msgpack_internal();
+ });
+ return result;
+ }
+
+ template<typename NumberType>
+ json get_msgpack_object(const NumberType len)
+ {
+ json result = value_t::object;
+ for (NumberType i = 0; i < len; ++i)
+ {
+ get();
+ auto key = get_msgpack_string();
+ (*result.m_value.object)[key] = parse_msgpack_internal();
+ }
+ return result;
+ }
+
+ /*!
+ @brief reads a UBJSON string
+
+ This function is either called after reading the 'S' byte explicitly
+ indicating a string, or in case of an object key where the 'S' byte can be
+ left out.
+
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+
+ @return string
+
+ @throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ std::string get_ubjson_string(const bool get_char = true);
+
+ /*!
+ @brief determine the type and size for a container
+
+ In the optimized UBJSON format, a type and a size can be provided to allow
+ for a more compact representation.
+
+ @return pair of the size and the type
+ */
+ std::pair<std::size_t, int> get_ubjson_size_type();
+
+ json get_ubjson_value(const int prefix);
+
+ json get_ubjson_array();
+
+ json get_ubjson_object();
+
+ /*!
+ @brief throw if end of input is not reached
+ @throw parse_error.110 if input not ended
+ */
+ void expect_eof() const
+ {
+ if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "expected end of input"));
+ }
+ }
+
+ /*!
+ @briefthrow if end of input is reached
+ @throw parse_error.110 if input ended
+ */
+ void unexpect_eof() const
+ {
+ if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+ }
+ }
+
+ private:
+ /// input adapter
+ raw_istream& is;
+
+ /// the current character
+ int current = std::char_traits<char>::eof();
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// whether we can assume little endianess
+ const bool is_little_endian = little_endianess();
+};
+
+json json::binary_reader::parse_cbor_internal(const bool get_char)
+{
+ switch (get_char ? get() : current)
+ {
+ // EOF
+ case std::char_traits<char>::eof():
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ // Integer 0x00..0x17 (0..23)
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ return static_cast<uint64_t>(current);
+
+ case 0x18: // Unsigned integer (one-byte uint8_t follows)
+ return get_number<uint8_t>();
+
+ case 0x19: // Unsigned integer (two-byte uint16_t follows)
+ return get_number<uint16_t>();
+
+ case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+ return get_number<uint32_t>();
+
+ case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+ return get_number<uint64_t>();
+
+ // Negative integer -1-0x00..-1-0x17 (-1..-24)
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ return static_cast<int8_t>(0x20 - 1 - current);
+
+ case 0x38: // Negative integer (one-byte uint8_t follows)
+ {
+ return static_cast<int64_t>(-1) - get_number<uint8_t>();
+ }
+
+ case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+ {
+ return static_cast<int64_t>(-1) - get_number<uint16_t>();
+ }
+
+ case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+ {
+ return static_cast<int64_t>(-1) - get_number<uint32_t>();
+ }
+
+ case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+ {
+ return static_cast<int64_t>(-1) -
+ static_cast<int64_t>(get_number<uint64_t>());
+ }
+
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ return get_cbor_string();
+ }
+
+ // array (0x00..0x17 data items follow)
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ {
+ return get_cbor_array(current & 0x1F);
+ }
+
+ case 0x98: // array (one-byte uint8_t for n follows)
+ {
+ return get_cbor_array(get_number<uint8_t>());
+ }
+
+ case 0x99: // array (two-byte uint16_t for n follow)
+ {
+ return get_cbor_array(get_number<uint16_t>());
+ }
+
+ case 0x9A: // array (four-byte uint32_t for n follow)
+ {
+ return get_cbor_array(get_number<uint32_t>());
+ }
+
+ case 0x9B: // array (eight-byte uint64_t for n follow)
+ {
+ return get_cbor_array(get_number<uint64_t>());
+ }
+
+ case 0x9F: // array (indefinite length)
+ {
+ json result = value_t::array;
+ while (get() != 0xFF)
+ {
+ result.push_back(parse_cbor_internal(false));
+ }
+ return result;
+ }
+
+ // map (0x00..0x17 pairs of data items follow)
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ {
+ return get_cbor_object(current & 0x1F);
+ }
+
+ case 0xB8: // map (one-byte uint8_t for n follows)
+ {
+ return get_cbor_object(get_number<uint8_t>());
+ }
+
+ case 0xB9: // map (two-byte uint16_t for n follow)
+ {
+ return get_cbor_object(get_number<uint16_t>());
+ }
+
+ case 0xBA: // map (four-byte uint32_t for n follow)
+ {
+ return get_cbor_object(get_number<uint32_t>());
+ }
+
+ case 0xBB: // map (eight-byte uint64_t for n follow)
+ {
+ return get_cbor_object(get_number<uint64_t>());
+ }
+
+ case 0xBF: // map (indefinite length)
+ {
+ json result = value_t::object;
+ while (get() != 0xFF)
+ {
+ auto key = get_cbor_string();
+ result[key] = parse_cbor_internal();
+ }
+ return result;
+ }
+
+ case 0xF4: // false
+ {
+ return false;
+ }
+
+ case 0xF5: // true
+ {
+ return true;
+ }
+
+ case 0xF6: // null
+ {
+ return value_t::null;
+ }
+
+ case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+ {
+ const int byte1 = get();
+ unexpect_eof();
+ const int byte2 = get();
+ unexpect_eof();
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added
+ // to IEEE 754 in 2008, today's programming platforms often
+ // still only have limited support for them. It is very
+ // easy to include at least decoding support for them even
+ // without such support. An example of a small decoder for
+ // half-precision floating-point numbers in the C language
+ // is shown in Fig. 3.
+ const int half = (byte1 << 8) + byte2;
+ const int exp = (half >> 10) & 0x1F;
+ const int mant = half & 0x3FF;
+ double val;
+ if (exp == 0)
+ {
+ val = std::ldexp(mant, -24);
+ }
+ else if (exp != 31)
+ {
+ val = std::ldexp(mant + 1024, exp - 25);
+ }
+ else
+ {
+ val = (mant == 0) ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
+ }
+ return (half & 0x8000) != 0 ? -val : val;
+ }
+
+ case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+ {
+ return get_number<float>();
+ }
+
+ case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ return get_number<double>();
+ }
+
+ default: // anything else (0xFF is handled inside the other types)
+ {
+ JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + Twine::utohexstr(current)));
+ }
+ }
+}
+
+json json::binary_reader::parse_msgpack_internal()
+{
+ switch (get())
+ {
+ // EOF
+ case std::char_traits<char>::eof():
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ // positive fixint
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ return static_cast<uint64_t>(current);
+
+ // fixmap
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ {
+ return get_msgpack_object(current & 0x0F);
+ }
+
+ // fixarray
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ case 0x98:
+ case 0x99:
+ case 0x9A:
+ case 0x9B:
+ case 0x9C:
+ case 0x9D:
+ case 0x9E:
+ case 0x9F:
+ {
+ return get_msgpack_array(current & 0x0F);
+ }
+
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ return get_msgpack_string();
+
+ case 0xC0: // nil
+ return value_t::null;
+
+ case 0xC2: // false
+ return false;
+
+ case 0xC3: // true
+ return true;
+
+ case 0xCA: // float 32
+ return get_number<float>();
+
+ case 0xCB: // float 64
+ return get_number<double>();
+
+ case 0xCC: // uint 8
+ return get_number<uint8_t>();
+
+ case 0xCD: // uint 16
+ return get_number<uint16_t>();
+
+ case 0xCE: // uint 32
+ return get_number<uint32_t>();
+
+ case 0xCF: // uint 64
+ return get_number<uint64_t>();
+
+ case 0xD0: // int 8
+ return get_number<int8_t>();
+
+ case 0xD1: // int 16
+ return get_number<int16_t>();
+
+ case 0xD2: // int 32
+ return get_number<int32_t>();
+
+ case 0xD3: // int 64
+ return get_number<int64_t>();
+
+ case 0xD9: // str 8
+ case 0xDA: // str 16
+ case 0xDB: // str 32
+ return get_msgpack_string();
+
+ case 0xDC: // array 16
+ {
+ return get_msgpack_array(get_number<uint16_t>());
+ }
+
+ case 0xDD: // array 32
+ {
+ return get_msgpack_array(get_number<uint32_t>());
+ }
+
+ case 0xDE: // map 16
+ {
+ return get_msgpack_object(get_number<uint16_t>());
+ }
+
+ case 0xDF: // map 32
+ {
+ return get_msgpack_object(get_number<uint32_t>());
+ }
+
+ // positive fixint
+ case 0xE0:
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xED:
+ case 0xEE:
+ case 0xEF:
+ case 0xF0:
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ case 0xF4:
+ case 0xF5:
+ case 0xF6:
+ case 0xF7:
+ case 0xF8:
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ case 0xFC:
+ case 0xFD:
+ case 0xFE:
+ case 0xFF:
+ return static_cast<int8_t>(current);
+
+ default: // anything else
+ {
+ JSON_THROW(parse_error::create(112, chars_read,
+ "error reading MessagePack; last byte: 0x" + Twine::utohexstr(current)));
+ }
+ }
+}
+
+std::string json::binary_reader::get_cbor_string()
+{
+ unexpect_eof();
+
+ switch (current)
+ {
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ {
+ return get_string(current & 0x1F);
+ }
+
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ {
+ return get_string(get_number<uint8_t>());
+ }
+
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ {
+ return get_string(get_number<uint16_t>());
+ }
+
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ {
+ return get_string(get_number<uint32_t>());
+ }
+
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ {
+ return get_string(get_number<uint64_t>());
+ }
+
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ std::string result;
+ while (get() != 0xFF)
+ {
+ result.append(get_cbor_string());
+ }
+ return result;
+ }
+
+ default:
+ {
+ JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + Twine::utohexstr(current)));
+ }
+ }
+}
+
+std::string json::binary_reader::get_msgpack_string()
+{
+ unexpect_eof();
+
+ switch (current)
+ {
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ {
+ return get_string(current & 0x1F);
+ }
+
+ case 0xD9: // str 8
+ {
+ return get_string(get_number<uint8_t>());
+ }
+
+ case 0xDA: // str 16
+ {
+ return get_string(get_number<uint16_t>());
+ }
+
+ case 0xDB: // str 32
+ {
+ return get_string(get_number<uint32_t>());
+ }
+
+ default:
+ {
+ JSON_THROW(parse_error::create(113, chars_read,
+ "expected a MessagePack string; last byte: 0x" + Twine::utohexstr(current)));
+ }
+ }
+}
+
+std::string json::binary_reader::get_ubjson_string(const bool get_char)
+{
+ if (get_char)
+ {
+ get(); // TODO: may we ignore N here?
+ }
+
+ unexpect_eof();
+
+ switch (current)
+ {
+ case 'U':
+ return get_string(get_number<uint8_t>());
+ case 'i':
+ return get_string(get_number<int8_t>());
+ case 'I':
+ return get_string(get_number<int16_t>());
+ case 'l':
+ return get_string(get_number<int32_t>());
+ case 'L':
+ return get_string(get_number<int64_t>());
+ default:
+ JSON_THROW(parse_error::create(113, chars_read,
+ "expected a UBJSON string; last byte: 0x" + Twine::utohexstr(current)));
+ }
+}
+
+std::pair<std::size_t, int> json::binary_reader::get_ubjson_size_type()
+{
+ std::size_t sz = std::string::npos;
+ int tc = 0;
+
+ get_ignore_noop();
+
+ if (current == '$')
+ {
+ tc = get(); // must not ignore 'N', because 'N' maybe the type
+ unexpect_eof();
+
+ get_ignore_noop();
+ if (current != '#')
+ {
+ JSON_THROW(parse_error::create(112, chars_read,
+ "expected '#' after UBJSON type information; last byte: 0x" + Twine::utohexstr(current)));
+ }
+ sz = parse_ubjson_internal();
+ }
+ else if (current == '#')
+ {
+ sz = parse_ubjson_internal();
+ }
+
+ return std::make_pair(sz, tc);
+}
+
+json json::binary_reader::get_ubjson_value(const int prefix)
+{
+ switch (prefix)
+ {
+ case std::char_traits<char>::eof(): // EOF
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ case 'T': // true
+ return true;
+ case 'F': // false
+ return false;
+
+ case 'Z': // null
+ return nullptr;
+
+ case 'U':
+ return get_number<uint8_t>();
+ case 'i':
+ return get_number<int8_t>();
+ case 'I':
+ return get_number<int16_t>();
+ case 'l':
+ return get_number<int32_t>();
+ case 'L':
+ return get_number<int64_t>();
+ case 'd':
+ return get_number<float>();
+ case 'D':
+ return get_number<double>();
+
+ case 'C': // char
+ {
+ get();
+ unexpect_eof();
+ if (JSON_UNLIKELY(current > 127))
+ {
+ JSON_THROW(parse_error::create(113, chars_read,
+ "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + Twine::utohexstr(current)));
+ }
+ return std::string(1, static_cast<char>(current));
+ }
+
+ case 'S': // string
+ return get_ubjson_string();
+
+ case '[': // array
+ return get_ubjson_array();
+
+ case '{': // object
+ return get_ubjson_object();
+
+ default: // anything else
+ JSON_THROW(parse_error::create(112, chars_read,
+ "error reading UBJSON; last byte: 0x" + Twine::utohexstr(current)));
+ }
+}
+
+json json::binary_reader::get_ubjson_array()
+{
+ json result = value_t::array;
+ const auto size_and_type = get_ubjson_size_type();
+
+ if (size_and_type.first != std::string::npos)
+ {
+ if (JSON_UNLIKELY(size_and_type.first > result.max_size()))
+ {
+ JSON_THROW(out_of_range::create(408,
+ "excessive array size: " + Twine(size_and_type.first)));
+ }
+
+ if (size_and_type.second != 0)
+ {
+ if (size_and_type.second != 'N')
+ {
+ std::generate_n(std::back_inserter(*result.m_value.array),
+ size_and_type.first, [this, size_and_type]()
+ {
+ return get_ubjson_value(size_and_type.second);
+ });
+ }
+ }
+ else
+ {
+ std::generate_n(std::back_inserter(*result.m_value.array),
+ size_and_type.first, [this]()
+ {
+ return parse_ubjson_internal();
+ });
+ }
+ }
+ else
+ {
+ while (current != ']')
+ {
+ result.push_back(parse_ubjson_internal(false));
+ get_ignore_noop();
+ }
+ }
+
+ return result;
+}
+
+json json::binary_reader::get_ubjson_object()
+{
+ json result = value_t::object;
+ const auto size_and_type = get_ubjson_size_type();
+
+ if (size_and_type.first != std::string::npos)
+ {
+ if (JSON_UNLIKELY(size_and_type.first > result.max_size()))
+ {
+ JSON_THROW(out_of_range::create(408,
+ "excessive object size: " + Twine(size_and_type.first)));
+ }
+
+ if (size_and_type.second != 0)
+ {
+ for (size_t i = 0; i < size_and_type.first; ++i)
+ {
+ auto key = get_ubjson_string();
+ (*result.m_value.object)[key] = get_ubjson_value(size_and_type.second);
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < size_and_type.first; ++i)
+ {
+ auto key = get_ubjson_string();
+ (*result.m_value.object)[key] = parse_ubjson_internal();
+ }
+ }
+ }
+ else
+ {
+ while (current != '}')
+ {
+ auto key = get_ubjson_string(false);
+ result[std::move(key)] = parse_ubjson_internal();
+ get_ignore_noop();
+ }
+ }
+
+ return result;
+}
+
+json json::from_cbor(raw_istream& is, const bool strict)
+{
+ return binary_reader(is).parse_cbor(strict);
+}
+
+json json::from_cbor(ArrayRef<uint8_t> arr, const bool strict)
+{
+ raw_mem_istream is(arr);
+ return from_cbor(is, strict);
+}
+
+json json::from_msgpack(raw_istream& is, const bool strict)
+{
+ return binary_reader(is).parse_msgpack(strict);
+}
+
+json json::from_msgpack(ArrayRef<uint8_t> arr, const bool strict)
+{
+ raw_mem_istream is(arr);
+ return from_msgpack(is, strict);
+}
+
+json json::from_ubjson(raw_istream& is, const bool strict)
+{
+ return binary_reader(is).parse_ubjson(strict);
+}
+
+json json::from_ubjson(ArrayRef<uint8_t> arr, const bool strict)
+{
+ raw_mem_istream is(arr);
+ return from_ubjson(is, strict);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_binary_writer.cpp b/wpiutil/src/main/native/cpp/json_binary_writer.cpp
new file mode 100644
index 0000000..2c0bbee
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json_binary_writer.cpp
@@ -0,0 +1,1090 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.1.2
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#define WPI_JSON_IMPLEMENTATION
+#include "wpi/json.h"
+
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+class json::binary_writer
+{
+ using CharType = unsigned char;
+
+ public:
+ /*!
+ @brief create a binary writer
+
+ @param[in] adapter output adapter to write to
+ */
+ explicit binary_writer(raw_ostream& s) : o(s)
+ {
+ }
+
+ /*!
+ @brief[in] j JSON value to serialize
+ */
+ void write_cbor(const json& j);
+
+ /*!
+ @brief[in] j JSON value to serialize
+ */
+ void write_msgpack(const json& j);
+
+ /*!
+ @param[in] j JSON value to serialize
+ @param[in] use_count whether to use '#' prefixes (optimized format)
+ @param[in] use_type whether to use '$' prefixes (optimized format)
+ @param[in] add_prefix whether prefixes need to be used for this value
+ */
+ void write_ubjson(const json& j, const bool use_count,
+ const bool use_type, const bool add_prefix = true);
+
+ private:
+ void write_cbor_string(StringRef str);
+
+ void write_msgpack_string(StringRef str);
+
+ /*
+ @brief write a number to output input
+
+ @param[in] n number of type @a NumberType
+ @tparam NumberType the type of the number
+
+ @note This function needs to respect the system's endianess, because bytes
+ in CBOR, MessagePack, and UBJSON are stored in network order (big
+ endian) and therefore need reordering on little endian systems.
+ */
+ template<typename NumberType>
+ void write_number(const NumberType n);
+
+ // UBJSON: write number (floating point)
+ template<typename NumberType, typename std::enable_if<
+ std::is_floating_point<NumberType>::value, int>::type = 0>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix)
+ {
+ if (add_prefix)
+ {
+ o << get_ubjson_float_prefix(n);
+ }
+ write_number(n);
+ }
+
+ // UBJSON: write number (unsigned integer)
+ template<typename NumberType, typename std::enable_if<
+ std::is_unsigned<NumberType>::value, int>::type = 0>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix);
+
+ // UBJSON: write number (signed integer)
+ template<typename NumberType, typename std::enable_if<
+ std::is_signed<NumberType>::value and
+ not std::is_floating_point<NumberType>::value, int>::type = 0>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix);
+
+ /*!
+ @brief determine the type prefix of container values
+
+ @note This function does not need to be 100% accurate when it comes to
+ integer limits. In case a number exceeds the limits of int64_t,
+ this will be detected by a later call to function
+ write_number_with_ubjson_prefix. Therefore, we return 'L' for any
+ value that does not fit the previous limits.
+ */
+ CharType ubjson_prefix(const json& j) const noexcept;
+
+ static constexpr CharType get_cbor_float_prefix(float)
+ {
+ return static_cast<CharType>(0xFA); // Single-Precision Float
+ }
+
+ static constexpr CharType get_cbor_float_prefix(double)
+ {
+ return static_cast<CharType>(0xFB); // Double-Precision Float
+ }
+
+ static constexpr CharType get_msgpack_float_prefix(float)
+ {
+ return static_cast<CharType>(0xCA); // float 32
+ }
+
+ static constexpr CharType get_msgpack_float_prefix(double)
+ {
+ return static_cast<CharType>(0xCB); // float 64
+ }
+
+ static constexpr CharType get_ubjson_float_prefix(float)
+ {
+ return 'd'; // float 32
+ }
+
+ static constexpr CharType get_ubjson_float_prefix(double)
+ {
+ return 'D'; // float 64
+ }
+
+ private:
+ static bool little_endianess(int num = 1) noexcept
+ {
+ return (*reinterpret_cast<char*>(&num) == 1);
+ }
+
+ /// whether we can assume little endianess
+ const bool is_little_endian = little_endianess();
+
+ /// the output
+ raw_ostream& o;
+};
+
+void json::binary_writer::write_cbor(const json& j)
+{
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ o << static_cast<CharType>(0xF6);
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ o << static_cast<CharType>(j.m_value.boolean ? 0xF5 : 0xF4);
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // CBOR does not differentiate between positive signed
+ // integers and unsigned integers. Therefore, we used the
+ // code from the value_t::number_unsigned case here.
+ if (j.m_value.number_integer <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
+ {
+ o << static_cast<CharType>(0x18);
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
+ {
+ o << static_cast<CharType>(0x19);
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
+ {
+ o << static_cast<CharType>(0x1A);
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else
+ {
+ o << static_cast<CharType>(0x1B);
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ // The conversions below encode the sign in the first
+ // byte, and the value is converted to a positive number.
+ const auto positive_number = -1 - j.m_value.number_integer;
+ if (j.m_value.number_integer >= -24)
+ {
+ write_number(static_cast<uint8_t>(0x20 + positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
+ {
+ o << static_cast<CharType>(0x38);
+ write_number(static_cast<uint8_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
+ {
+ o << static_cast<CharType>(0x39);
+ write_number(static_cast<uint16_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
+ {
+ o << static_cast<CharType>(0x3A);
+ write_number(static_cast<uint32_t>(positive_number));
+ }
+ else
+ {
+ o << static_cast<CharType>(0x3B);
+ write_number(static_cast<uint64_t>(positive_number));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ o << static_cast<CharType>(0x18);
+ write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ o << static_cast<CharType>(0x19);
+ write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ o << static_cast<CharType>(0x1A);
+ write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
+ }
+ else
+ {
+ o << static_cast<CharType>(0x1B);
+ write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ o << get_cbor_float_prefix(j.m_value.number_float);
+ write_number(j.m_value.number_float);
+ break;
+ }
+
+ case value_t::string:
+ {
+ write_cbor_string(*j.m_value.string);
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0x80 + N));
+ }
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
+ {
+ o << static_cast<CharType>(0x98);
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
+ {
+ o << static_cast<CharType>(0x99);
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
+ {
+ o << static_cast<CharType>(0x9A);
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<uint64_t>::max)())
+ {
+ o << static_cast<CharType>(0x9B);
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_cbor(el);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0xA0 + N));
+ }
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
+ {
+ o << static_cast<CharType>(0xB8);
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
+ {
+ o << static_cast<CharType>(0xB9);
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
+ {
+ o << static_cast<CharType>(0xBA);
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else /*if (N <= (std::numeric_limits<uint64_t>::max)())*/
+ {
+ o << static_cast<CharType>(0xBB);
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_cbor_string(el.first());
+ write_cbor(el.second);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void json::binary_writer::write_msgpack(const json& j)
+{
+ switch (j.type())
+ {
+ case value_t::null: // nil
+ {
+ o << static_cast<CharType>(0xC0);
+ break;
+ }
+
+ case value_t::boolean: // true and false
+ {
+ o << static_cast<CharType>(j.m_value.boolean ? 0xC3 : 0xC2);
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // MessagePack does not differentiate between positive
+ // signed integers and unsigned integers. Therefore, we used
+ // the code from the value_t::number_unsigned case here.
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8
+ o << static_cast<CharType>(0xCC);
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16
+ o << static_cast<CharType>(0xCD);
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32
+ o << static_cast<CharType>(0xCE);
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
+ {
+ // uint 64
+ o << static_cast<CharType>(0xCF);
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ if (j.m_value.number_integer >= -32)
+ {
+ // negative fixnum
+ write_number(static_cast<int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
+ {
+ // int 8
+ o << static_cast<CharType>(0xD0);
+ write_number(static_cast<int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
+ {
+ // int 16
+ o << static_cast<CharType>(0xD1);
+ write_number(static_cast<int16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
+ {
+ // int 32
+ o << static_cast<CharType>(0xD2);
+ write_number(static_cast<int32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
+ {
+ // int 64
+ o << static_cast<CharType>(0xD3);
+ write_number(static_cast<int64_t>(j.m_value.number_integer));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8
+ o << static_cast<CharType>(0xCC);
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16
+ o << static_cast<CharType>(0xCD);
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32
+ o << static_cast<CharType>(0xCE);
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
+ {
+ // uint 64
+ o << static_cast<CharType>(0xCF);
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ o << get_msgpack_float_prefix(j.m_value.number_float);
+ write_number(j.m_value.number_float);
+ break;
+ }
+
+ case value_t::string:
+ {
+ write_msgpack_string(*j.m_value.string);
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 15)
+ {
+ // fixarray
+ write_number(static_cast<uint8_t>(0x90 | N));
+ }
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // array 16
+ o << static_cast<CharType>(0xDC);
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // array 32
+ o << static_cast<CharType>(0xDD);
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_msgpack(el);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 15)
+ {
+ // fixmap
+ write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
+ }
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // map 16
+ o << static_cast<CharType>(0xDE);
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // map 32
+ o << static_cast<CharType>(0xDF);
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_msgpack_string(el.first());
+ write_msgpack(el.second);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void json::binary_writer::write_ubjson(const json& j, const bool use_count,
+ const bool use_type, const bool add_prefix)
+{
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('Z');
+ }
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ if (add_prefix)
+ o << static_cast<CharType>(j.m_value.boolean ? 'T' : 'F');
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);
+ break;
+ }
+
+ case value_t::string:
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('S');
+ }
+ write_number_with_ubjson_prefix(j.m_value.string->size(), true);
+ o << j.m_value.string;
+ break;
+ }
+
+ case value_t::array:
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('[');
+ }
+
+ bool prefix_required = true;
+ if (use_type and not j.m_value.array->empty())
+ {
+ assert(use_count);
+ const CharType first_prefix = ubjson_prefix(j.front());
+ const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
+ [this, first_prefix](const json & v)
+ {
+ return ubjson_prefix(v) == first_prefix;
+ });
+
+ if (same_prefix)
+ {
+ prefix_required = false;
+ o << static_cast<CharType>('$');
+ o << first_prefix;
+ }
+ }
+
+ if (use_count)
+ {
+ o << static_cast<CharType>('#');
+ write_number_with_ubjson_prefix(j.m_value.array->size(), true);
+ }
+
+ for (const auto& el : *j.m_value.array)
+ {
+ write_ubjson(el, use_count, use_type, prefix_required);
+ }
+
+ if (not use_count)
+ {
+ o << static_cast<CharType>(']');
+ }
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('{');
+ }
+
+ bool prefix_required = true;
+ if (use_type and not j.m_value.object->empty())
+ {
+ assert(use_count);
+ const CharType first_prefix = ubjson_prefix(j.front());
+ const bool same_prefix = std::all_of(j.begin(), j.end(),
+ [this, first_prefix](const json & v)
+ {
+ return ubjson_prefix(v) == first_prefix;
+ });
+
+ if (same_prefix)
+ {
+ prefix_required = false;
+ o << static_cast<CharType>('$');
+ o << first_prefix;
+ }
+ }
+
+ if (use_count)
+ {
+ o << static_cast<CharType>('#');
+ write_number_with_ubjson_prefix(j.m_value.object->size(), true);
+ }
+
+ for (const auto& el : *j.m_value.object)
+ {
+ write_number_with_ubjson_prefix(el.first().size(), true);
+ o << el.first();
+ write_ubjson(el.second, use_count, use_type, prefix_required);
+ }
+
+ if (not use_count)
+ {
+ o << static_cast<CharType>('}');
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void json::binary_writer::write_cbor_string(StringRef str)
+{
+ // step 1: write control byte and the string length
+ const auto N = str.size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0x60 + N));
+ }
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
+ {
+ o << static_cast<CharType>(0x78);
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
+ {
+ o << static_cast<CharType>(0x79);
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
+ {
+ o << static_cast<CharType>(0x7A);
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<uint64_t>::max)())
+ {
+ o << static_cast<CharType>(0x7B);
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write the string
+ o << str;
+}
+
+void json::binary_writer::write_msgpack_string(StringRef str)
+{
+ // step 1: write control byte and the string length
+ const auto N = str.size();
+ if (N <= 31)
+ {
+ // fixstr
+ write_number(static_cast<uint8_t>(0xA0 | N));
+ }
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // str 8
+ o << static_cast<CharType>(0xD9);
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // str 16
+ o << static_cast<CharType>(0xDA);
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // str 32
+ o << static_cast<CharType>(0xDB);
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write the string
+ o << str;
+}
+
+template<typename NumberType>
+void json::binary_writer::write_number(const NumberType n)
+{
+ // step 1: write number to array of length NumberType
+ std::array<uint8_t, sizeof(NumberType)> vec;
+ std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+ // step 2: write array to output (with possible reordering)
+ if (is_little_endian)
+ {
+ // reverse byte order prior to conversion if necessary
+ std::reverse(vec.begin(), vec.end());
+ }
+
+ o << ArrayRef<uint8_t>(vec.data(), sizeof(NumberType));
+}
+
+template<typename NumberType, typename std::enable_if<
+ std::is_unsigned<NumberType>::value, int>::type>
+void json::binary_writer::write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix)
+{
+ if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('i'); // int8
+ }
+ write_number(static_cast<uint8_t>(n));
+ }
+ else if (n <= (std::numeric_limits<uint8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('U'); // uint8
+ }
+ write_number(static_cast<uint8_t>(n));
+ }
+ else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('I'); // int16
+ }
+ write_number(static_cast<int16_t>(n));
+ }
+ else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('l'); // int32
+ }
+ write_number(static_cast<int32_t>(n));
+ }
+ else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('L'); // int64
+ }
+ write_number(static_cast<int64_t>(n));
+ }
+ else
+ {
+ JSON_THROW(out_of_range::create(407, "number overflow serializing " + Twine(n)));
+ }
+}
+
+template<typename NumberType, typename std::enable_if<
+ std::is_signed<NumberType>::value and
+ not std::is_floating_point<NumberType>::value, int>::type>
+void json::binary_writer::write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix)
+{
+ if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('i'); // int8
+ }
+ write_number(static_cast<int8_t>(n));
+ }
+ else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('U'); // uint8
+ }
+ write_number(static_cast<uint8_t>(n));
+ }
+ else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('I'); // int16
+ }
+ write_number(static_cast<int16_t>(n));
+ }
+ else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('l'); // int32
+ }
+ write_number(static_cast<int32_t>(n));
+ }
+ else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
+ {
+ if (add_prefix)
+ {
+ o << static_cast<CharType>('L'); // int64
+ }
+ write_number(static_cast<int64_t>(n));
+ }
+ // LCOV_EXCL_START
+ else
+ {
+ JSON_THROW(out_of_range::create(407, "number overflow serializing " + Twine(n)));
+ }
+ // LCOV_EXCL_STOP
+}
+
+json::binary_writer::CharType json::binary_writer::ubjson_prefix(const json& j) const noexcept
+{
+ switch (j.type())
+ {
+ case value_t::null:
+ return 'Z';
+
+ case value_t::boolean:
+ return j.m_value.boolean ? 'T' : 'F';
+
+ case value_t::number_integer:
+ {
+ if ((std::numeric_limits<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
+ {
+ return 'i';
+ }
+ else if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
+ {
+ return 'U';
+ }
+ else if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
+ {
+ return 'I';
+ }
+ else if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
+ {
+ return 'l';
+ }
+ else // no check and assume int64_t (see note above)
+ {
+ return 'L';
+ }
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
+ {
+ return 'i';
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ return 'U';
+ }
+ else if (j.m_value.number_unsigned <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
+ {
+ return 'I';
+ }
+ else if (j.m_value.number_unsigned <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
+ {
+ return 'l';
+ }
+ else // no check and assume int64_t (see note above)
+ {
+ return 'L';
+ }
+ }
+
+ case value_t::number_float:
+ return get_ubjson_float_prefix(j.m_value.number_float);
+
+ case value_t::string:
+ return 'S';
+
+ case value_t::array:
+ return '[';
+
+ case value_t::object:
+ return '{';
+
+ default: // discarded values
+ return 'N';
+ }
+}
+
+std::vector<uint8_t> json::to_cbor(const json& j)
+{
+ std::vector<uint8_t> result;
+ raw_uvector_ostream os(result);
+ to_cbor(os, j);
+ return result;
+}
+
+ArrayRef<uint8_t> json::to_cbor(const json& j, std::vector<uint8_t>& buf)
+{
+ buf.clear();
+ raw_uvector_ostream os(buf);
+ to_cbor(os, j);
+ return os.array();
+}
+
+ArrayRef<uint8_t> json::to_cbor(const json& j, SmallVectorImpl<uint8_t>& buf)
+{
+ buf.clear();
+ raw_usvector_ostream os(buf);
+ to_cbor(os, j);
+ return os.array();
+}
+
+void json::to_cbor(raw_ostream& os, const json& j)
+{
+ binary_writer(os).write_cbor(j);
+}
+
+std::vector<uint8_t> json::to_msgpack(const json& j)
+{
+ std::vector<uint8_t> result;
+ raw_uvector_ostream os(result);
+ to_msgpack(os, j);
+ return result;
+}
+
+ArrayRef<uint8_t> json::to_msgpack(const json& j, std::vector<uint8_t>& buf)
+{
+ buf.clear();
+ raw_uvector_ostream os(buf);
+ to_msgpack(os, j);
+ return os.array();
+}
+
+ArrayRef<uint8_t> json::to_msgpack(const json& j, SmallVectorImpl<uint8_t>& buf)
+{
+ buf.clear();
+ raw_usvector_ostream os(buf);
+ to_msgpack(os, j);
+ return os.array();
+}
+
+void json::to_msgpack(raw_ostream& os, const json& j)
+{
+ binary_writer(os).write_msgpack(j);
+}
+
+std::vector<uint8_t> json::to_ubjson(const json& j,
+ const bool use_size,
+ const bool use_type)
+{
+ std::vector<uint8_t> result;
+ raw_uvector_ostream os(result);
+ to_ubjson(os, j, use_size, use_type);
+ return result;
+}
+
+ArrayRef<uint8_t> json::to_ubjson(const json& j, std::vector<uint8_t>& buf,
+ const bool use_size, const bool use_type)
+{
+ buf.clear();
+ raw_uvector_ostream os(buf);
+ to_ubjson(os, j, use_size, use_type);
+ return os.array();
+}
+
+ArrayRef<uint8_t> json::to_ubjson(const json& j, SmallVectorImpl<uint8_t>& buf,
+ const bool use_size, const bool use_type)
+{
+ buf.clear();
+ raw_usvector_ostream os(buf);
+ to_ubjson(os, j, use_size, use_type);
+ return os.array();
+}
+
+void json::to_ubjson(raw_ostream& os, const json& j,
+ const bool use_size, const bool use_type)
+{
+ binary_writer(os).write_ubjson(j, use_size, use_type);
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_parser.cpp b/wpiutil/src/main/native/cpp/json_parser.cpp
new file mode 100644
index 0000000..e0d7db3
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json_parser.cpp
@@ -0,0 +1,1968 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.1.2
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#define WPI_JSON_IMPLEMENTATION
+#include "wpi/json.h"
+
+#include <clocale>
+#include <cmath>
+#include <cstdlib>
+
+#include "wpi/Format.h"
+#include "wpi/SmallString.h"
+#include "wpi/raw_istream.h"
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+class json::lexer
+{
+ public:
+ /// token types for the parser
+ enum class token_type
+ {
+ uninitialized, ///< indicating the scanner is uninitialized
+ literal_true, ///< the `true` literal
+ literal_false, ///< the `false` literal
+ literal_null, ///< the `null` literal
+ value_string, ///< a string -- use get_string() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
+ value_integer, ///< a signed integer -- use get_number_integer() for actual value
+ value_float, ///< an floating point number -- use get_number_float() for actual value
+ begin_array, ///< the character for array begin `[`
+ begin_object, ///< the character for object begin `{`
+ end_array, ///< the character for array end `]`
+ end_object, ///< the character for object end `}`
+ name_separator, ///< the name separator `:`
+ value_separator, ///< the value separator `,`
+ parse_error, ///< indicating a parse error
+ end_of_input, ///< indicating the end of the input buffer
+ literal_or_value ///< a literal or the begin of a value (only for diagnostics)
+ };
+
+ /// return name of values of type token_type (only used for errors)
+ static const char* token_type_name(const token_type t) noexcept;
+
+ explicit lexer(raw_istream& s);
+
+ // delete because of pointer members
+ lexer(const lexer&) = delete;
+ lexer& operator=(lexer&) = delete;
+
+ private:
+ /////////////////////
+ // locales
+ /////////////////////
+
+ /// return the locale-dependent decimal point
+ static char get_decimal_point() noexcept
+ {
+ const auto loc = localeconv();
+ assert(loc != nullptr);
+ return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+ }
+
+ /////////////////////
+ // scan functions
+ /////////////////////
+
+ /*!
+ @brief get codepoint from 4 hex characters following `\u`
+
+ For input "\u c1 c2 c3 c4" the codepoint is:
+ (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+ = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+ Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+ must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+ conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+ between the ASCII value of the character and the desired integer value.
+
+ @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+ non-hex character)
+ */
+ int get_codepoint();
+
+ /*!
+ @brief check if the next byte(s) are inside a given range
+
+ Adds the current byte and, for each passed range, reads a new byte and
+ checks if it is inside the range. If a violation was detected, set up an
+ error message and return false. Otherwise, return true.
+
+ @param[in] ranges list of integers; interpreted as list of pairs of
+ inclusive lower and upper bound, respectively
+
+ @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+ 1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+ @return true if and only if no range violation was detected
+ */
+ bool next_byte_in_range(std::initializer_list<int> ranges)
+ {
+ assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
+ add(current);
+
+ for (auto range = ranges.begin(); range != ranges.end(); ++range)
+ {
+ get();
+ if (JSON_LIKELY(*range <= current and current <= *(++range)))
+ {
+ add(current);
+ }
+ else
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief scan a string literal
+
+ This function scans a string according to Sect. 7 of RFC 7159. While
+ scanning, bytes are escaped and copied into buffer token_buffer. Then the
+ function returns successfully, token_buffer is *not* null-terminated (as it
+ may contain \0 bytes), and token_buffer.size() is the number of bytes in the
+ string.
+
+ @return token_type::value_string if string could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note In case of errors, variable error_message contains a textual
+ description.
+ */
+ token_type scan_string();
+
+ static void strtof(float& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ static void strtof(double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ static void strtof(long double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ /*!
+ @brief scan a number literal
+
+ This function scans a string according to Sect. 6 of RFC 7159.
+
+ The function is realized with a deterministic finite state machine derived
+ from the grammar described in RFC 7159. Starting in state "init", the
+ input is read and used to determined the next state. Only state "done"
+ accepts the number. State "error" is a trap state to model errors. In the
+ table below, "anything" means any character but the ones listed before.
+
+ state | 0 | 1-9 | e E | + | - | . | anything
+ ---------|----------|----------|----------|---------|---------|----------|-----------
+ init | zero | any1 | [error] | [error] | minus | [error] | [error]
+ minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
+ zero | done | done | exponent | done | done | decimal1 | done
+ any1 | any1 | any1 | exponent | done | done | decimal1 | done
+ decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error]
+ decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
+ exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
+ sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
+ any2 | any2 | any2 | done | done | done | done | done
+
+ The state machine is realized with one label per state (prefixed with
+ "scan_number_") and `goto` statements between them. The state machine
+ contains cycles, but any cycle can be left when EOF is read. Therefore,
+ the function is guaranteed to terminate.
+
+ During scanning, the read bytes are stored in token_buffer. This string is
+ then converted to a signed integer, an unsigned integer, or a
+ floating-point number.
+
+ @return token_type::value_unsigned, token_type::value_integer, or
+ token_type::value_float if number could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note The scanner is independent of the current locale. Internally, the
+ locale's decimal point is used instead of `.` to work with the
+ locale-dependent converters.
+ */
+ token_type scan_number();
+
+ /*!
+ @param[in] literal_text the literal text to expect
+ @param[in] length the length of the passed literal text
+ @param[in] return_type the token type to return on success
+ */
+ token_type scan_literal(const char* literal_text, const std::size_t length,
+ token_type return_type);
+
+ /////////////////////
+ // input management
+ /////////////////////
+
+ /// reset token_buffer; current character is beginning of token
+ void reset() noexcept
+ {
+ token_buffer.clear();
+ token_string.clear();
+ token_string.push_back(std::char_traits<char>::to_char_type(current));
+ }
+
+ /*
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a
+ `std::char_traits<char>::eof()` in that case. Stores the scanned characters
+ for use in error messages.
+
+ @return character read from the input
+ */
+ std::char_traits<char>::int_type get()
+ {
+ ++chars_read;
+ if (JSON_UNLIKELY(!unget_chars.empty()))
+ {
+ current = unget_chars.back();
+ unget_chars.pop_back();
+ token_string.push_back(current);
+ return current;
+ }
+ char c;
+ is.read(c);
+ if (JSON_UNLIKELY(is.has_error()))
+ {
+ current = std::char_traits<char>::eof();
+ }
+ else
+ {
+ current = std::char_traits<char>::to_int_type(c);
+ token_string.push_back(c);
+ }
+ return current;
+ }
+
+ /// unget current character (return it again on next get)
+ void unget()
+ {
+ --chars_read;
+ if (JSON_LIKELY(current != std::char_traits<char>::eof()))
+ {
+ unget_chars.emplace_back(current);
+ assert(token_string.size() != 0);
+ token_string.pop_back();
+ if (!token_string.empty())
+ {
+ current = token_string.back();
+ }
+ }
+ }
+
+ /// put back character (returned on next get)
+ void putback(std::char_traits<char>::int_type c)
+ {
+ --chars_read;
+ unget_chars.emplace_back(c);
+ }
+
+ /// add a character to token_buffer
+ void add(int c)
+ {
+ token_buffer.push_back(std::char_traits<char>::to_char_type(c));
+ }
+
+ public:
+ /////////////////////
+ // value getters
+ /////////////////////
+
+ /// return integer value
+ int64_t get_number_integer() const noexcept
+ {
+ return value_integer;
+ }
+
+ /// return unsigned integer value
+ uint64_t get_number_unsigned() const noexcept
+ {
+ return value_unsigned;
+ }
+
+ /// return floating-point value
+ double get_number_float() const noexcept
+ {
+ return value_float;
+ }
+
+ /// return current string value
+ StringRef get_string()
+ {
+ return token_buffer;
+ }
+
+ /////////////////////
+ // diagnostics
+ /////////////////////
+
+ /// return position of last read token
+ std::size_t get_position() const noexcept
+ {
+ return chars_read;
+ }
+
+ /// return the last read token (for errors only). Will never contain EOF
+ /// (an arbitrary value that is not a valid char value, often -1), because
+ /// 255 may legitimately occur. May contain NUL, which should be escaped.
+ std::string get_token_string() const;
+
+ /// return syntax error message
+ const char* get_error_message() const noexcept
+ {
+ return error_message;
+ }
+
+ /////////////////////
+ // actual scanner
+ /////////////////////
+
+ token_type scan();
+
+ private:
+ /// input adapter
+ raw_istream& is;
+
+ /// the current character
+ std::char_traits<char>::int_type current = std::char_traits<char>::eof();
+
+ /// unget characters
+ SmallVector<std::char_traits<char>::int_type, 4> unget_chars;
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// raw input token string (for error messages)
+ SmallString<128> token_string {};
+
+ /// buffer for variable-length tokens (numbers, strings)
+ SmallString<128> token_buffer {};
+
+ /// a description of occurred lexer errors
+ const char* error_message = "";
+
+ // number values
+ int64_t value_integer = 0;
+ uint64_t value_unsigned = 0;
+ double value_float = 0;
+
+ /// the decimal point
+ const char decimal_point_char = '.';
+};
+
+////////////
+// parser //
+////////////
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive decent parser.
+*/
+class json::parser
+{
+ using lexer_t = json::lexer;
+ using token_type = typename lexer_t::token_type;
+
+ public:
+ /// a parser reading from an input adapter
+ explicit parser(raw_istream& s,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions_ = true)
+ : callback(cb), m_lexer(s), allow_exceptions(allow_exceptions_)
+ {}
+
+ /*!
+ @brief public parser interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @param[in,out] result parsed JSON value
+
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse(const bool strict, json& result);
+
+ /*!
+ @brief public accept interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @return whether the input is a proper JSON text
+ */
+ bool accept(const bool strict = true)
+ {
+ // read first token
+ get_token();
+
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // strict => last token must be EOF
+ return not strict or (get_token() == token_type::end_of_input);
+ }
+
+ private:
+ /*!
+ @brief the actual parser
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse_internal(bool keep, json& result);
+
+ /*!
+ @brief the actual acceptor
+
+ @invariant 1. The last token is not yet processed. Therefore, the caller
+ of this function must make sure a token has been read.
+ 2. When this function returns, the last token is processed.
+ That is, the last read character was already considered.
+
+ This invariant makes sure that no token needs to be "unput".
+ */
+ bool accept_internal();
+
+ /// get next token from lexer
+ token_type get_token()
+ {
+ return (last_token = m_lexer.scan());
+ }
+
+ /*!
+ @throw parse_error.101 if expected token did not occur
+ */
+ bool expect(token_type t)
+ {
+ if (JSON_UNLIKELY(t != last_token))
+ {
+ errored = true;
+ expected = t;
+ if (allow_exceptions)
+ {
+ throw_exception();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ [[noreturn]] void throw_exception() const;
+
+ private:
+ /// current level of recursion
+ int depth = 0;
+ /// callback function
+ const parser_callback_t callback = nullptr;
+ /// the type of the last read token
+ token_type last_token = token_type::uninitialized;
+ /// the lexer
+ lexer_t m_lexer;
+ /// whether a syntax error occurred
+ bool errored = false;
+ /// possible reason for the syntax error
+ token_type expected = token_type::uninitialized;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+};
+
+const char* json::lexer::token_type_name(const token_type t) noexcept
+{
+ switch (t)
+ {
+ case token_type::uninitialized:
+ return "<uninitialized>";
+ case token_type::literal_true:
+ return "true literal";
+ case token_type::literal_false:
+ return "false literal";
+ case token_type::literal_null:
+ return "null literal";
+ case token_type::value_string:
+ return "string literal";
+ case lexer::token_type::value_unsigned:
+ case lexer::token_type::value_integer:
+ case lexer::token_type::value_float:
+ return "number literal";
+ case token_type::begin_array:
+ return "'['";
+ case token_type::begin_object:
+ return "'{'";
+ case token_type::end_array:
+ return "']'";
+ case token_type::end_object:
+ return "'}'";
+ case token_type::name_separator:
+ return "':'";
+ case token_type::value_separator:
+ return "','";
+ case token_type::parse_error:
+ return "<parse error>";
+ case token_type::end_of_input:
+ return "end of input";
+ case token_type::literal_or_value:
+ return "'[', '{', or a literal";
+ default: // catch non-enum values
+ return "unknown token"; // LCOV_EXCL_LINE
+ }
+}
+
+json::lexer::lexer(raw_istream& s)
+ : is(s), decimal_point_char(get_decimal_point())
+{
+ // skip byte order mark
+ std::char_traits<char>::int_type c;
+ if ((c = get()) == 0xEF)
+ {
+ if ((c = get()) == 0xBB)
+ {
+ if ((c = get()) == 0xBF)
+ {
+ chars_read = 0;
+ return; // Ignore BOM
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ unget();
+ }
+ putback('\xBB');
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ unget();
+ }
+ putback('\xEF');
+ }
+ unget(); // no byte order mark; process as usual
+}
+
+int json::lexer::get_codepoint()
+{
+ // this function only makes sense after reading `\u`
+ assert(current == 'u');
+ int codepoint = 0;
+
+ const auto factors = { 12, 8, 4, 0 };
+ for (const auto factor : factors)
+ {
+ get();
+
+ if (current >= '0' and current <= '9')
+ {
+ codepoint += ((current - 0x30) << factor);
+ }
+ else if (current >= 'A' and current <= 'F')
+ {
+ codepoint += ((current - 0x37) << factor);
+ }
+ else if (current >= 'a' and current <= 'f')
+ {
+ codepoint += ((current - 0x57) << factor);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
+ return codepoint;
+}
+
+json::lexer::token_type json::lexer::scan_string()
+{
+ // reset token_buffer (ignore opening quote)
+ reset();
+
+ // we entered the function by reading an open quote
+ assert(current == '\"');
+
+ while (true)
+ {
+ // get next character
+ switch (get())
+ {
+ // end of file while parsing string
+ case std::char_traits<char>::eof():
+ {
+ error_message = "invalid string: missing closing quote";
+ return token_type::parse_error;
+ }
+
+ // closing quote
+ case '\"':
+ {
+ return token_type::value_string;
+ }
+
+ // escapes
+ case '\\':
+ {
+ switch (get())
+ {
+ // quotation mark
+ case '\"':
+ add('\"');
+ break;
+ // reverse solidus
+ case '\\':
+ add('\\');
+ break;
+ // solidus
+ case '/':
+ add('/');
+ break;
+ // backspace
+ case 'b':
+ add('\b');
+ break;
+ // form feed
+ case 'f':
+ add('\f');
+ break;
+ // line feed
+ case 'n':
+ add('\n');
+ break;
+ // carriage return
+ case 'r':
+ add('\r');
+ break;
+ // tab
+ case 't':
+ add('\t');
+ break;
+
+ // unicode escapes
+ case 'u':
+ {
+ const int codepoint1 = get_codepoint();
+ int codepoint = codepoint1; // start with codepoint1
+
+ if (JSON_UNLIKELY(codepoint1 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if code point is a high surrogate
+ if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)
+ {
+ // expect next \uxxxx entry
+ if (JSON_LIKELY(get() == '\\' and get() == 'u'))
+ {
+ const int codepoint2 = get_codepoint();
+
+ if (JSON_UNLIKELY(codepoint2 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if codepoint2 is a low surrogate
+ if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))
+ {
+ // overwrite codepoint
+ codepoint =
+ // high surrogate occupies the most significant 22 bits
+ (codepoint1 << 10)
+ // low surrogate occupies the least significant 15 bits
+ + codepoint2
+ // there is still the 0xD800, 0xDC00 and 0x10000 noise
+ // in the result so we have to subtract with:
+ // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+ - 0x35FDC00;
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+ return token_type::parse_error;
+ }
+ }
+
+ // result of the above calculation yields a proper codepoint
+ assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
+
+ // translate codepoint into bytes
+ if (codepoint < 0x80)
+ {
+ // 1-byte characters: 0xxxxxxx (ASCII)
+ add(codepoint);
+ }
+ else if (codepoint <= 0x7FF)
+ {
+ // 2-byte characters: 110xxxxx 10xxxxxx
+ add(0xC0 | (codepoint >> 6));
+ add(0x80 | (codepoint & 0x3F));
+ }
+ else if (codepoint <= 0xFFFF)
+ {
+ // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+ add(0xE0 | (codepoint >> 12));
+ add(0x80 | ((codepoint >> 6) & 0x3F));
+ add(0x80 | (codepoint & 0x3F));
+ }
+ else
+ {
+ // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ add(0xF0 | (codepoint >> 18));
+ add(0x80 | ((codepoint >> 12) & 0x3F));
+ add(0x80 | ((codepoint >> 6) & 0x3F));
+ add(0x80 | (codepoint & 0x3F));
+ }
+
+ break;
+ }
+
+ // other characters after escape
+ default:
+ error_message = "invalid string: forbidden character after backslash";
+ return token_type::parse_error;
+ }
+
+ break;
+ }
+
+ // invalid control characters
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ {
+ error_message = "invalid string: control character must be escaped";
+ return token_type::parse_error;
+ }
+
+ // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+ case 0x20:
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ {
+ add(current);
+ break;
+ }
+
+ // U+0080..U+07FF: bytes C2..DF 80..BF
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD5:
+ case 0xD6:
+ case 0xD7:
+ case 0xD8:
+ case 0xD9:
+ case 0xDA:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xDE:
+ case 0xDF:
+ {
+ if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+ case 0xE0:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+ // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xEE:
+ case 0xEF:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+ case 0xED:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+ case 0xF0:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+ case 0xF4:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // remaining bytes (80..C1 and F5..FF) are ill-formed
+ default:
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return token_type::parse_error;
+ }
+ }
+ }
+}
+
+json::lexer::token_type json::lexer::scan_number()
+{
+ // reset token_buffer to store the number's bytes
+ reset();
+
+ // the type of the parsed number; initially set to unsigned; will be
+ // changed if minus sign, decimal point or exponent is read
+ token_type number_type = token_type::value_unsigned;
+
+ // state (init): we just found out we need to scan a number
+ switch (current)
+ {
+ case '-':
+ {
+ add(current);
+ goto scan_number_minus;
+ }
+
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ // all other characters are rejected outside scan_number()
+ assert(false); // LCOV_EXCL_LINE
+ }
+ }
+
+scan_number_minus:
+ // state: we just parsed a leading minus sign
+ number_type = token_type::value_integer;
+ switch (get())
+ {
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '-'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_zero:
+ // state: we just parse a zero (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_any1:
+ // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_decimal1:
+ // state: we just parsed a decimal point
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '.'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_decimal2:
+ // we just parsed at least one number after a decimal point
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_exponent:
+ // we just parsed an exponent
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '+':
+ case '-':
+ {
+ add(current);
+ goto scan_number_sign;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message =
+ "invalid number; expected '+', '-', or digit after exponent";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_sign:
+ // we just parsed an exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after exponent sign";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_any2:
+ // we just parsed a number after the exponent or exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_done:
+ // unget the character after the number (we only read it to know that
+ // we are done scanning a number)
+ unget();
+
+ char* endptr = nullptr;
+ errno = 0;
+
+ // try to parse integers first and fall back to floats
+ if (number_type == token_type::value_unsigned)
+ {
+ const auto x = std::strtoull(token_buffer.c_str(), &endptr, 10);
+
+ // we checked the number format before
+ assert(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno == 0)
+ {
+ value_unsigned = static_cast<uint64_t>(x);
+ if (value_unsigned == x)
+ {
+ return token_type::value_unsigned;
+ }
+ }
+ }
+ else if (number_type == token_type::value_integer)
+ {
+ const auto x = std::strtoll(token_buffer.c_str(), &endptr, 10);
+
+ // we checked the number format before
+ assert(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno == 0)
+ {
+ value_integer = static_cast<int64_t>(x);
+ if (value_integer == x)
+ {
+ return token_type::value_integer;
+ }
+ }
+ }
+
+ // this code is reached if we parse a floating-point number or if an
+ // integer conversion above failed
+ strtof(value_float, token_buffer.c_str(), &endptr);
+
+ // we checked the number format before
+ assert(endptr == token_buffer.data() + token_buffer.size());
+
+ return token_type::value_float;
+}
+
+json::lexer::token_type json::lexer::scan_literal(const char* literal_text, const std::size_t length,
+ token_type return_type)
+{
+ assert(current == literal_text[0]);
+ for (std::size_t i = 1; i < length; ++i)
+ {
+ if (JSON_UNLIKELY(get() != literal_text[i]))
+ {
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+ return return_type;
+}
+
+std::string json::lexer::get_token_string() const
+{
+ // escape control characters
+ std::string result;
+ raw_string_ostream ss(result);
+ for (const unsigned char c : token_string)
+ {
+ if (c <= '\x1F')
+ {
+ // escape control characters
+ ss << "<U+" << format_hex_no_prefix(c, 4, true) << '>';
+ }
+ else
+ {
+ // add character as is
+ ss << c;
+ }
+ }
+
+ ss.flush();
+ return result;
+}
+
+json::lexer::token_type json::lexer::scan()
+{
+ // read next character and ignore whitespace
+ do
+ {
+ get();
+ }
+ while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
+
+ switch (current)
+ {
+ // structural characters
+ case '[':
+ return token_type::begin_array;
+ case ']':
+ return token_type::end_array;
+ case '{':
+ return token_type::begin_object;
+ case '}':
+ return token_type::end_object;
+ case ':':
+ return token_type::name_separator;
+ case ',':
+ return token_type::value_separator;
+
+ // literals
+ case 't':
+ return scan_literal("true", 4, token_type::literal_true);
+ case 'f':
+ return scan_literal("false", 5, token_type::literal_false);
+ case 'n':
+ return scan_literal("null", 4, token_type::literal_null);
+
+ // string
+ case '\"':
+ return scan_string();
+
+ // number
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return scan_number();
+
+ // end of input (the null byte is needed when parsing from
+ // string literals)
+ case '\0':
+ case std::char_traits<char>::eof():
+ return token_type::end_of_input;
+
+ // error
+ default:
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+}
+
+void json::parser::parse(const bool strict, json& result)
+{
+ // read first token
+ get_token();
+
+ parse_internal(true, result);
+ result.assert_invariant();
+
+ // in strict mode, input must be completely read
+ if (strict)
+ {
+ get_token();
+ expect(token_type::end_of_input);
+ }
+
+ // in case of an error, return discarded value
+ if (errored)
+ {
+ result = value_t::discarded;
+ return;
+ }
+
+ // set top-level value to null if it was discarded by the callback
+ // function
+ if (result.is_discarded())
+ {
+ result = nullptr;
+ }
+}
+
+void json::parser::parse_internal(bool keep, json& result)
+{
+ // never parse after a parse error was detected
+ assert(not errored);
+
+ // start with a discarded value
+ if (not result.is_discarded())
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ if (keep)
+ {
+ if (callback)
+ {
+ keep = callback(depth++, parse_event_t::object_start, result);
+ }
+
+ if (not callback or keep)
+ {
+ // explicitly set result to object to cope with {}
+ result.m_type = value_t::object;
+ result.m_value = value_t::object;
+ }
+ }
+
+ // read next token
+ get_token();
+
+ // closing } -> we are done
+ if (last_token == token_type::end_object)
+ {
+ if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ // parse values
+ SmallString<128> key;
+ json value;
+ while (true)
+ {
+ // store key
+ if (not expect(token_type::value_string))
+ {
+ return;
+ }
+ key = m_lexer.get_string();
+
+ bool keep_tag = false;
+ if (keep)
+ {
+ if (callback)
+ {
+ json k(key);
+ keep_tag = callback(depth, parse_event_t::key, k);
+ }
+ else
+ {
+ keep_tag = true;
+ }
+ }
+
+ // parse separator (:)
+ get_token();
+ if (not expect(token_type::name_separator))
+ {
+ return;
+ }
+
+ // parse and add value
+ get_token();
+ value.m_value.destroy(value.m_type);
+ value.m_type = value_t::discarded;
+ parse_internal(keep, value);
+
+ if (JSON_UNLIKELY(errored))
+ {
+ return;
+ }
+
+ if (keep and keep_tag and not value.is_discarded())
+ {
+ result.m_value.object->try_emplace(StringRef(key.data(), key.size()), std::move(value));
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing }
+ if (not expect(token_type::end_object))
+ {
+ return;
+ }
+ break;
+ }
+
+ if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ case token_type::begin_array:
+ {
+ if (keep)
+ {
+ if (callback)
+ {
+ keep = callback(depth++, parse_event_t::array_start, result);
+ }
+
+ if (not callback or keep)
+ {
+ // explicitly set result to array to cope with []
+ result.m_type = value_t::array;
+ result.m_value = value_t::array;
+ }
+ }
+
+ // read next token
+ get_token();
+
+ // closing ] -> we are done
+ if (last_token == token_type::end_array)
+ {
+ if (callback and not callback(--depth, parse_event_t::array_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ // parse values
+ json value;
+ while (true)
+ {
+ // parse value
+ value.m_value.destroy(value.m_type);
+ value.m_type = value_t::discarded;
+ parse_internal(keep, value);
+
+ if (JSON_UNLIKELY(errored))
+ {
+ return;
+ }
+
+ if (keep and not value.is_discarded())
+ {
+ result.m_value.array->push_back(std::move(value));
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ if (not expect(token_type::end_array))
+ {
+ return;
+ }
+ break;
+ }
+
+ if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ case token_type::literal_null:
+ {
+ result.m_type = value_t::null;
+ break;
+ }
+
+ case token_type::value_string:
+ {
+ result.m_type = value_t::string;
+ result.m_value = m_lexer.get_string();
+ break;
+ }
+
+ case token_type::literal_true:
+ {
+ result.m_type = value_t::boolean;
+ result.m_value = true;
+ break;
+ }
+
+ case token_type::literal_false:
+ {
+ result.m_type = value_t::boolean;
+ result.m_value = false;
+ break;
+ }
+
+ case token_type::value_unsigned:
+ {
+ result.m_type = value_t::number_unsigned;
+ result.m_value = m_lexer.get_number_unsigned();
+ break;
+ }
+
+ case token_type::value_integer:
+ {
+ result.m_type = value_t::number_integer;
+ result.m_value = m_lexer.get_number_integer();
+ break;
+ }
+
+ case token_type::value_float:
+ {
+ result.m_type = value_t::number_float;
+ result.m_value = m_lexer.get_number_float();
+
+ // throw in case of infinity or NAN
+ if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float)))
+ {
+ if (allow_exceptions)
+ {
+ JSON_THROW(out_of_range::create(406, "number overflow parsing '" +
+ Twine(m_lexer.get_token_string()) + "'"));
+ }
+ expect(token_type::uninitialized);
+ }
+ break;
+ }
+
+ case token_type::parse_error:
+ {
+ // using "uninitialized" to avoid "expected" message
+ if (not expect(token_type::uninitialized))
+ {
+ return;
+ }
+ break; // LCOV_EXCL_LINE
+ }
+
+ default:
+ {
+ // the last token was unexpected; we expected a value
+ if (not expect(token_type::literal_or_value))
+ {
+ return;
+ }
+ break; // LCOV_EXCL_LINE
+ }
+ }
+
+ if (keep and callback and not callback(depth, parse_event_t::value, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+}
+
+bool json::parser::accept_internal()
+{
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ // read next token
+ get_token();
+
+ // closing } -> we are done
+ if (last_token == token_type::end_object)
+ {
+ return true;
+ }
+
+ // parse values
+ while (true)
+ {
+ // parse key
+ if (last_token != token_type::value_string)
+ {
+ return false;
+ }
+
+ // parse separator (:)
+ get_token();
+ if (last_token != token_type::name_separator)
+ {
+ return false;
+ }
+
+ // parse value
+ get_token();
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing }
+ return (last_token == token_type::end_object);
+ }
+ }
+
+ case token_type::begin_array:
+ {
+ // read next token
+ get_token();
+
+ // closing ] -> we are done
+ if (last_token == token_type::end_array)
+ {
+ return true;
+ }
+
+ // parse values
+ while (true)
+ {
+ // parse value
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ return (last_token == token_type::end_array);
+ }
+ }
+
+ case token_type::value_float:
+ {
+ // reject infinity or NAN
+ return std::isfinite(m_lexer.get_number_float());
+ }
+
+ case token_type::literal_false:
+ case token_type::literal_null:
+ case token_type::literal_true:
+ case token_type::value_integer:
+ case token_type::value_string:
+ case token_type::value_unsigned:
+ return true;
+
+ default: // the last token was unexpected
+ return false;
+ }
+}
+
+void json::parser::throw_exception() const
+{
+ std::string error_msg = "syntax error - ";
+ if (last_token == token_type::parse_error)
+ {
+ error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
+ m_lexer.get_token_string() + "'";
+ }
+ else
+ {
+ error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
+ }
+
+ if (expected != token_type::uninitialized)
+ {
+ error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
+ }
+
+ JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
+}
+
+json json::parse(StringRef s,
+ const parser_callback_t cb,
+ const bool allow_exceptions)
+{
+ raw_mem_istream is(makeArrayRef(s.data(), s.size()));
+ return parse(is, cb, allow_exceptions);
+}
+
+json json::parse(ArrayRef<uint8_t> arr,
+ const parser_callback_t cb,
+ const bool allow_exceptions)
+{
+ raw_mem_istream is(arr);
+ return parse(is, cb, allow_exceptions);
+}
+
+json json::parse(raw_istream& i,
+ const parser_callback_t cb,
+ const bool allow_exceptions)
+{
+ json result;
+ parser(i, cb, allow_exceptions).parse(true, result);
+ return result;
+}
+
+bool json::accept(StringRef s)
+{
+ raw_mem_istream is(makeArrayRef(s.data(), s.size()));
+ return parser(is).accept(true);
+}
+
+bool json::accept(ArrayRef<uint8_t> arr)
+{
+ raw_mem_istream is(arr);
+ return parser(is).accept(true);
+}
+
+bool json::accept(raw_istream& i)
+{
+ return parser(i).accept(true);
+}
+
+raw_istream& operator>>(raw_istream& i, json& j)
+{
+ json::parser(i).parse(false, j);
+ return i;
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_pointer.cpp b/wpiutil/src/main/native/cpp/json_pointer.cpp
new file mode 100644
index 0000000..9cb1ec4
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json_pointer.cpp
@@ -0,0 +1,544 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.1.2
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#define WPI_JSON_IMPLEMENTATION
+#include "wpi/json.h"
+
+#include <numeric> // accumulate
+
+#include "wpi/SmallString.h"
+
+namespace wpi {
+
+std::string json_pointer::to_string() const noexcept
+{
+ return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+ std::string{},
+ [](const std::string & a, const std::string & b)
+ {
+ return a + "/" + escape(b);
+ });
+}
+
+int json_pointer::array_index(const Twine& s)
+{
+ SmallString<128> buf;
+ StringRef str = s.toNullTerminatedStringRef(buf);
+ std::size_t processed_chars = 0;
+ const int res = std::stoi(str, &processed_chars);
+
+ // check if the string was completely read
+ if (JSON_UNLIKELY(processed_chars != str.size()))
+ {
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(s) + "'"));
+ }
+
+ return res;
+}
+
+json& json_pointer::get_and_create(json& j) const
+{
+ using size_type = typename json::size_type;
+ auto result = &j;
+
+ // in case no reference tokens exist, return a reference to the JSON value
+ // j which will be overwritten by a primitive value
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (result->m_type)
+ {
+ case detail::value_t::null:
+ {
+ if (reference_token == "0")
+ {
+ // start a new array if reference token is 0
+ result = &result->operator[](0);
+ }
+ else
+ {
+ // start a new object otherwise
+ result = &result->operator[](reference_token);
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ // create an entry in the object
+ result = &result->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // create an entry in the array
+ JSON_TRY
+ {
+ result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ }
+ break;
+ }
+
+ /*
+ The following code is only reached if there exists a reference
+ token _and_ the current value is primitive. In this case, we have
+ an error situation, because primitive values may only occur as
+ single value; that is, with an empty list of reference tokens.
+ */
+ default:
+ JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
+ }
+ }
+
+ return *result;
+}
+
+json& json_pointer::get_unchecked(json* ptr) const
+{
+ using size_type = typename json::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ // convert null values to arrays or objects before continuing
+ if (ptr->m_type == detail::value_t::null)
+ {
+ // check if reference token is a number
+ const bool nums =
+ std::all_of(reference_token.begin(), reference_token.end(),
+ [](const char x)
+ {
+ return (x >= '0' and x <= '9');
+ });
+
+ // change value to array for numbers or "-" or to object otherwise
+ *ptr = (nums or reference_token == "-")
+ ? detail::value_t::array
+ : detail::value_t::object;
+ }
+
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + Twine(reference_token) +
+ "' must not begin with '0'"));
+ }
+
+ if (reference_token == "-")
+ {
+ // explicitly treat "-" as index beyond the end
+ ptr = &ptr->operator[](ptr->m_value.array->size());
+ }
+ else
+ {
+ // convert array index to number; unchecked access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ }
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+json& json_pointer::get_checked(json* ptr) const
+{
+ using size_type = typename json::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + Twine(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + Twine(reference_token) +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+const json& json_pointer::get_unchecked(const json* ptr) const
+{
+ using size_type = typename json::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" cannot be used for const access
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + Twine(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + Twine(reference_token) +
+ "' must not begin with '0'"));
+ }
+
+ // use unchecked array access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+const json& json_pointer::get_checked(const json* ptr) const
+{
+ using size_type = typename json::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + Twine(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + Twine(reference_token) +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + Twine(reference_token) + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + Twine(reference_token) + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+std::vector<std::string> json_pointer::split(const Twine& reference_string)
+{
+ SmallString<128> ref_str_buf;
+ StringRef ref_str = reference_string.toStringRef(ref_str_buf);
+ std::vector<std::string> result;
+
+ // special case: empty reference string -> no reference tokens
+ if (ref_str.empty())
+ {
+ return result;
+ }
+
+ // check if nonempty reference string begins with slash
+ if (JSON_UNLIKELY(ref_str[0] != '/'))
+ {
+ JSON_THROW(detail::parse_error::create(107, 1,
+ "JSON pointer must be empty or begin with '/' - was: '" +
+ Twine(ref_str) + "'"));
+ }
+
+ // extract the reference tokens:
+ // - slash: position of the last read slash (or end of string)
+ // - start: position after the previous slash
+ for (
+ // search for the first slash after the first character
+ std::size_t slash = ref_str.find_first_of('/', 1),
+ // set the beginning of the first reference token
+ start = 1;
+ // we can stop if start == string::npos+1 = 0
+ start != 0;
+ // set the beginning of the next reference token
+ // (will eventually be 0 if slash == std::string::npos)
+ start = slash + 1,
+ // find next slash
+ slash = ref_str.find_first_of('/', start))
+ {
+ // use the text between the beginning of the reference token
+ // (start) and the last slash (slash).
+ auto reference_token = ref_str.slice(start, slash);
+
+ // check reference tokens are properly escaped
+ for (std::size_t pos = reference_token.find_first_of('~');
+ pos != StringRef::npos;
+ pos = reference_token.find_first_of('~', pos + 1))
+ {
+ assert(reference_token[pos] == '~');
+
+ // ~ must be followed by 0 or 1
+ if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
+ (reference_token[pos + 1] != '0' and
+ reference_token[pos + 1] != '1')))
+ {
+ JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
+ }
+ }
+
+ // finally, store the reference token
+ std::string ref_tok = reference_token;
+ unescape(ref_tok);
+ result.emplace_back(std::move(ref_tok));
+ }
+
+ return result;
+}
+
+void json_pointer::replace_substring(std::string& s, const std::string& f,
+ const std::string& t)
+{
+ assert(not f.empty());
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != std::string::npos; // make sure f was found
+ s.replace(pos, f.size(), t), // replace with t, and
+ pos = s.find(f, pos + t.size())) // find next occurrence of f
+ {}
+}
+
+std::string json_pointer::escape(std::string s)
+{
+ replace_substring(s, "~", "~0");
+ replace_substring(s, "/", "~1");
+ return s;
+}
+
+/// unescape "~1" to tilde and "~0" to slash (order is important!)
+void json_pointer::unescape(std::string& s)
+{
+ replace_substring(s, "~1", "/");
+ replace_substring(s, "~0", "~");
+}
+
+void json_pointer::flatten(const Twine& reference_string,
+ const json& value,
+ json& result)
+{
+ switch (value.m_type)
+ {
+ case detail::value_t::array:
+ {
+ if (value.m_value.array->empty())
+ {
+ // flatten empty array as null
+ SmallString<64> buf;
+ result[reference_string.toStringRef(buf)] = nullptr;
+ }
+ else
+ {
+ // iterate array and use index as reference string
+ for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
+ {
+ flatten(reference_string + "/" + Twine(i),
+ value.m_value.array->operator[](i), result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ if (value.m_value.object->empty())
+ {
+ // flatten empty object as null
+ SmallString<64> buf;
+ result[reference_string.toStringRef(buf)] = nullptr;
+ }
+ else
+ {
+ // iterate object and use keys as reference string
+ for (const auto& element : *value.m_value.object)
+ {
+ flatten(reference_string + "/" + Twine(escape(element.first())), element.second, result);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ // add primitive value with its reference string
+ SmallString<64> buf;
+ result[reference_string.toStringRef(buf)] = value;
+ break;
+ }
+ }
+}
+
+json
+json_pointer::unflatten(const json& value)
+{
+ if (JSON_UNLIKELY(not value.is_object()))
+ {
+ JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
+ }
+
+ // we need to iterate over the object values in sorted key order
+ SmallVector<StringMapConstIterator<json>, 64> sorted;
+ for (auto i = value.m_value.object->begin(),
+ end = value.m_value.object->end(); i != end; ++i)
+ {
+ if (!i->second.is_primitive())
+ {
+ JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
+ }
+ sorted.push_back(i);
+ }
+ std::sort(sorted.begin(), sorted.end(),
+ [](const StringMapConstIterator<json>& a,
+ const StringMapConstIterator<json>& b) {
+ return a->getKey() < b->getKey();
+ });
+
+ json result;
+
+ // iterate the sorted JSON object values
+ for (const auto& element : sorted)
+ {
+
+ // assign value to reference pointed to by JSON pointer; Note
+ // that if the JSON pointer is "" (i.e., points to the whole
+ // value), function get_and_create returns a reference to
+ // result itself. An assignment will then create a primitive
+ // value.
+ json_pointer(element->first()).get_and_create(result) = element->second;
+ }
+
+ return result;
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_serializer.cpp b/wpiutil/src/main/native/cpp/json_serializer.cpp
new file mode 100644
index 0000000..8ab92b1
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json_serializer.cpp
@@ -0,0 +1,1532 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.1.2
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#define WPI_JSON_IMPLEMENTATION
+#include "wpi/json.h"
+
+#include "wpi/Format.h"
+#include "wpi/SmallString.h"
+#include "wpi/StringExtras.h"
+#include "wpi/raw_os_ostream.h"
+
+#include "json_serializer.h"
+
+namespace wpi {
+
+namespace {
+
+/*!
+@brief implements the Grisu2 algorithm for binary to decimal floating-point
+conversion.
+
+This implementation is a slightly modified version of the reference
+implementation which may be obtained from
+http://florian.loitsch.com/publications (bench.tar.gz).
+
+The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
+
+For a detailed description of the algorithm see:
+
+[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
+ Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
+ Language Design and Implementation, PLDI 2010
+[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
+ Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
+ Design and Implementation, PLDI 1996
+*/
+namespace dtoa_impl
+{
+
+template <typename Target, typename Source>
+Target reinterpret_bits(const Source source)
+{
+ static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
+
+ Target target;
+ std::memcpy(&target, &source, sizeof(Source));
+ return target;
+}
+
+struct diyfp // f * 2^e
+{
+ static constexpr int kPrecision = 64; // = q
+
+ uint64_t f;
+ int e;
+
+ constexpr diyfp() noexcept : f(0), e(0) {}
+ constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
+
+ /*!
+ @brief returns x - y
+ @pre x.e == y.e and x.f >= y.f
+ */
+ static diyfp sub(const diyfp& x, const diyfp& y) noexcept
+ {
+ assert(x.e == y.e);
+ assert(x.f >= y.f);
+
+ return diyfp(x.f - y.f, x.e);
+ }
+
+ /*!
+ @brief returns x * y
+ @note The result is rounded. (Only the upper q bits are returned.)
+ */
+ static diyfp mul(const diyfp& x, const diyfp& y) noexcept
+ {
+ static_assert(kPrecision == 64, "internal error");
+
+ // Computes:
+ // f = round((x.f * y.f) / 2^q)
+ // e = x.e + y.e + q
+
+ // Emulate the 64-bit * 64-bit multiplication:
+ //
+ // p = u * v
+ // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
+ // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi )
+ // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 )
+ // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 )
+ // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3)
+ // = (p0_lo ) + 2^32 (Q ) + 2^64 (H )
+ // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H )
+ //
+ // (Since Q might be larger than 2^32 - 1)
+ //
+ // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
+ //
+ // (Q_hi + H does not overflow a 64-bit int)
+ //
+ // = p_lo + 2^64 p_hi
+
+ const uint64_t u_lo = x.f & 0xFFFFFFFF;
+ const uint64_t u_hi = x.f >> 32;
+ const uint64_t v_lo = y.f & 0xFFFFFFFF;
+ const uint64_t v_hi = y.f >> 32;
+
+ const uint64_t p0 = u_lo * v_lo;
+ const uint64_t p1 = u_lo * v_hi;
+ const uint64_t p2 = u_hi * v_lo;
+ const uint64_t p3 = u_hi * v_hi;
+
+ const uint64_t p0_hi = p0 >> 32;
+ const uint64_t p1_lo = p1 & 0xFFFFFFFF;
+ const uint64_t p1_hi = p1 >> 32;
+ const uint64_t p2_lo = p2 & 0xFFFFFFFF;
+ const uint64_t p2_hi = p2 >> 32;
+
+ uint64_t Q = p0_hi + p1_lo + p2_lo;
+
+ // The full product might now be computed as
+ //
+ // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
+ // p_lo = p0_lo + (Q << 32)
+ //
+ // But in this particular case here, the full p_lo is not required.
+ // Effectively we only need to add the highest bit in p_lo to p_hi (and
+ // Q_hi + 1 does not overflow).
+
+ Q += uint64_t{1} << (64 - 32 - 1); // round, ties up
+
+ const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32);
+
+ return diyfp(h, x.e + y.e + 64);
+ }
+
+ /*!
+ @brief normalize x such that the significand is >= 2^(q-1)
+ @pre x.f != 0
+ */
+ static diyfp normalize(diyfp x) noexcept
+ {
+ assert(x.f != 0);
+
+ while ((x.f >> 63) == 0)
+ {
+ x.f <<= 1;
+ x.e--;
+ }
+
+ return x;
+ }
+
+ /*!
+ @brief normalize x such that the result has the exponent E
+ @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
+ */
+ static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
+ {
+ const int delta = x.e - target_exponent;
+
+ assert(delta >= 0);
+ assert(((x.f << delta) >> delta) == x.f);
+
+ return diyfp(x.f << delta, target_exponent);
+ }
+};
+
+struct boundaries
+{
+ diyfp w;
+ diyfp minus;
+ diyfp plus;
+};
+
+/*!
+Compute the (normalized) diyfp representing the input number 'value' and its
+boundaries.
+
+@pre value must be finite and positive
+*/
+template <typename FloatType>
+boundaries compute_boundaries(FloatType value)
+{
+ assert(std::isfinite(value));
+ assert(value > 0);
+
+ // Convert the IEEE representation into a diyfp.
+ //
+ // If v is denormal:
+ // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1))
+ // If v is normalized:
+ // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
+
+ static_assert(std::numeric_limits<FloatType>::is_iec559,
+ "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
+
+ constexpr int kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
+ constexpr int kBias = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
+ constexpr int kMinExp = 1 - kBias;
+ constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
+
+ using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type;
+
+ const uint64_t bits = reinterpret_bits<bits_type>(value);
+ const uint64_t E = bits >> (kPrecision - 1);
+ const uint64_t F = bits & (kHiddenBit - 1);
+
+ const bool is_denormal = (E == 0);
+ const diyfp v = is_denormal
+ ? diyfp(F, kMinExp)
+ : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
+
+ // Compute the boundaries m- and m+ of the floating-point value
+ // v = f * 2^e.
+ //
+ // Determine v- and v+, the floating-point predecessor and successor if v,
+ // respectively.
+ //
+ // v- = v - 2^e if f != 2^(p-1) or e == e_min (A)
+ // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B)
+ //
+ // v+ = v + 2^e
+ //
+ // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
+ // between m- and m+ round to v, regardless of how the input rounding
+ // algorithm breaks ties.
+ //
+ // ---+-------------+-------------+-------------+-------------+--- (A)
+ // v- m- v m+ v+
+ //
+ // -----------------+------+------+-------------+-------------+--- (B)
+ // v- m- v m+ v+
+
+ const bool lower_boundary_is_closer = (F == 0 and E > 1);
+ const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
+ const diyfp m_minus = lower_boundary_is_closer
+ ? diyfp(4 * v.f - 1, v.e - 2) // (B)
+ : diyfp(2 * v.f - 1, v.e - 1); // (A)
+
+ // Determine the normalized w+ = m+.
+ const diyfp w_plus = diyfp::normalize(m_plus);
+
+ // Determine w- = m- such that e_(w-) = e_(w+).
+ const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
+
+ return {diyfp::normalize(v), w_minus, w_plus};
+}
+
+// Given normalized diyfp w, Grisu needs to find a (normalized) cached
+// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
+// within a certain range [alpha, gamma] (Definition 3.2 from [1])
+//
+// alpha <= e = e_c + e_w + q <= gamma
+//
+// or
+//
+// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
+// <= f_c * f_w * 2^gamma
+//
+// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
+//
+// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
+//
+// or
+//
+// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
+//
+// The choice of (alpha,gamma) determines the size of the table and the form of
+// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
+// in practice:
+//
+// The idea is to cut the number c * w = f * 2^e into two parts, which can be
+// processed independently: An integral part p1, and a fractional part p2:
+//
+// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
+// = (f div 2^-e) + (f mod 2^-e) * 2^e
+// = p1 + p2 * 2^e
+//
+// The conversion of p1 into decimal form requires a series of divisions and
+// modulos by (a power of) 10. These operations are faster for 32-bit than for
+// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
+// achieved by choosing
+//
+// -e >= 32 or e <= -32 := gamma
+//
+// In order to convert the fractional part
+//
+// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
+//
+// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
+// d[-i] are extracted in order:
+//
+// (10 * p2) div 2^-e = d[-1]
+// (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
+//
+// The multiplication by 10 must not overflow. It is sufficient to choose
+//
+// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
+//
+// Since p2 = f mod 2^-e < 2^-e,
+//
+// -e <= 60 or e >= -60 := alpha
+
+constexpr int kAlpha = -60;
+constexpr int kGamma = -32;
+
+struct cached_power // c = f * 2^e ~= 10^k
+{
+ uint64_t f;
+ int e;
+ int k;
+};
+
+/*!
+For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
+power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
+satisfies (Definition 3.2 from [1])
+
+ alpha <= e_c + e + q <= gamma.
+*/
+inline cached_power get_cached_power_for_binary_exponent(int e)
+{
+ // Now
+ //
+ // alpha <= e_c + e + q <= gamma (1)
+ // ==> f_c * 2^alpha <= c * 2^e * 2^q
+ //
+ // and since the c's are normalized, 2^(q-1) <= f_c,
+ //
+ // ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
+ // ==> 2^(alpha - e - 1) <= c
+ //
+ // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as
+ //
+ // k = ceil( log_10( 2^(alpha - e - 1) ) )
+ // = ceil( (alpha - e - 1) * log_10(2) )
+ //
+ // From the paper:
+ // "In theory the result of the procedure could be wrong since c is rounded,
+ // and the computation itself is approximated [...]. In practice, however,
+ // this simple function is sufficient."
+ //
+ // For IEEE double precision floating-point numbers converted into
+ // normalized diyfp's w = f * 2^e, with q = 64,
+ //
+ // e >= -1022 (min IEEE exponent)
+ // -52 (p - 1)
+ // -52 (p - 1, possibly normalize denormal IEEE numbers)
+ // -11 (normalize the diyfp)
+ // = -1137
+ //
+ // and
+ //
+ // e <= +1023 (max IEEE exponent)
+ // -52 (p - 1)
+ // -11 (normalize the diyfp)
+ // = 960
+ //
+ // This binary exponent range [-1137,960] results in a decimal exponent
+ // range [-307,324]. One does not need to store a cached power for each
+ // k in this range. For each such k it suffices to find a cached power
+ // such that the exponent of the product lies in [alpha,gamma].
+ // This implies that the difference of the decimal exponents of adjacent
+ // table entries must be less than or equal to
+ //
+ // floor( (gamma - alpha) * log_10(2) ) = 8.
+ //
+ // (A smaller distance gamma-alpha would require a larger table.)
+
+ // NB:
+ // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
+
+ constexpr int kCachedPowersSize = 79;
+ constexpr int kCachedPowersMinDecExp = -300;
+ constexpr int kCachedPowersDecStep = 8;
+
+ static constexpr cached_power kCachedPowers[] =
+ {
+ { 0xAB70FE17C79AC6CA, -1060, -300 },
+ { 0xFF77B1FCBEBCDC4F, -1034, -292 },
+ { 0xBE5691EF416BD60C, -1007, -284 },
+ { 0x8DD01FAD907FFC3C, -980, -276 },
+ { 0xD3515C2831559A83, -954, -268 },
+ { 0x9D71AC8FADA6C9B5, -927, -260 },
+ { 0xEA9C227723EE8BCB, -901, -252 },
+ { 0xAECC49914078536D, -874, -244 },
+ { 0x823C12795DB6CE57, -847, -236 },
+ { 0xC21094364DFB5637, -821, -228 },
+ { 0x9096EA6F3848984F, -794, -220 },
+ { 0xD77485CB25823AC7, -768, -212 },
+ { 0xA086CFCD97BF97F4, -741, -204 },
+ { 0xEF340A98172AACE5, -715, -196 },
+ { 0xB23867FB2A35B28E, -688, -188 },
+ { 0x84C8D4DFD2C63F3B, -661, -180 },
+ { 0xC5DD44271AD3CDBA, -635, -172 },
+ { 0x936B9FCEBB25C996, -608, -164 },
+ { 0xDBAC6C247D62A584, -582, -156 },
+ { 0xA3AB66580D5FDAF6, -555, -148 },
+ { 0xF3E2F893DEC3F126, -529, -140 },
+ { 0xB5B5ADA8AAFF80B8, -502, -132 },
+ { 0x87625F056C7C4A8B, -475, -124 },
+ { 0xC9BCFF6034C13053, -449, -116 },
+ { 0x964E858C91BA2655, -422, -108 },
+ { 0xDFF9772470297EBD, -396, -100 },
+ { 0xA6DFBD9FB8E5B88F, -369, -92 },
+ { 0xF8A95FCF88747D94, -343, -84 },
+ { 0xB94470938FA89BCF, -316, -76 },
+ { 0x8A08F0F8BF0F156B, -289, -68 },
+ { 0xCDB02555653131B6, -263, -60 },
+ { 0x993FE2C6D07B7FAC, -236, -52 },
+ { 0xE45C10C42A2B3B06, -210, -44 },
+ { 0xAA242499697392D3, -183, -36 },
+ { 0xFD87B5F28300CA0E, -157, -28 },
+ { 0xBCE5086492111AEB, -130, -20 },
+ { 0x8CBCCC096F5088CC, -103, -12 },
+ { 0xD1B71758E219652C, -77, -4 },
+ { 0x9C40000000000000, -50, 4 },
+ { 0xE8D4A51000000000, -24, 12 },
+ { 0xAD78EBC5AC620000, 3, 20 },
+ { 0x813F3978F8940984, 30, 28 },
+ { 0xC097CE7BC90715B3, 56, 36 },
+ { 0x8F7E32CE7BEA5C70, 83, 44 },
+ { 0xD5D238A4ABE98068, 109, 52 },
+ { 0x9F4F2726179A2245, 136, 60 },
+ { 0xED63A231D4C4FB27, 162, 68 },
+ { 0xB0DE65388CC8ADA8, 189, 76 },
+ { 0x83C7088E1AAB65DB, 216, 84 },
+ { 0xC45D1DF942711D9A, 242, 92 },
+ { 0x924D692CA61BE758, 269, 100 },
+ { 0xDA01EE641A708DEA, 295, 108 },
+ { 0xA26DA3999AEF774A, 322, 116 },
+ { 0xF209787BB47D6B85, 348, 124 },
+ { 0xB454E4A179DD1877, 375, 132 },
+ { 0x865B86925B9BC5C2, 402, 140 },
+ { 0xC83553C5C8965D3D, 428, 148 },
+ { 0x952AB45CFA97A0B3, 455, 156 },
+ { 0xDE469FBD99A05FE3, 481, 164 },
+ { 0xA59BC234DB398C25, 508, 172 },
+ { 0xF6C69A72A3989F5C, 534, 180 },
+ { 0xB7DCBF5354E9BECE, 561, 188 },
+ { 0x88FCF317F22241E2, 588, 196 },
+ { 0xCC20CE9BD35C78A5, 614, 204 },
+ { 0x98165AF37B2153DF, 641, 212 },
+ { 0xE2A0B5DC971F303A, 667, 220 },
+ { 0xA8D9D1535CE3B396, 694, 228 },
+ { 0xFB9B7CD9A4A7443C, 720, 236 },
+ { 0xBB764C4CA7A44410, 747, 244 },
+ { 0x8BAB8EEFB6409C1A, 774, 252 },
+ { 0xD01FEF10A657842C, 800, 260 },
+ { 0x9B10A4E5E9913129, 827, 268 },
+ { 0xE7109BFBA19C0C9D, 853, 276 },
+ { 0xAC2820D9623BF429, 880, 284 },
+ { 0x80444B5E7AA7CF85, 907, 292 },
+ { 0xBF21E44003ACDD2D, 933, 300 },
+ { 0x8E679C2F5E44FF8F, 960, 308 },
+ { 0xD433179D9C8CB841, 986, 316 },
+ { 0x9E19DB92B4E31BA9, 1013, 324 },
+ };
+
+ // This computation gives exactly the same results for k as
+ // k = ceil((kAlpha - e - 1) * 0.30102999566398114)
+ // for |e| <= 1500, but doesn't require floating-point operations.
+ // NB: log_10(2) ~= 78913 / 2^18
+ assert(e >= -1500);
+ assert(e <= 1500);
+ const int f = kAlpha - e - 1;
+ const int k = (f * 78913) / (1 << 18) + (f > 0);
+
+ const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
+ assert(index >= 0);
+ assert(index < kCachedPowersSize);
+ static_cast<void>(kCachedPowersSize); // Fix warning.
+
+ const cached_power cached = kCachedPowers[index];
+ assert(kAlpha <= cached.e + e + 64);
+ assert(kGamma >= cached.e + e + 64);
+
+ return cached;
+}
+
+/*!
+For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
+For n == 0, returns 1 and sets pow10 := 1.
+*/
+inline int find_largest_pow10(const uint32_t n, uint32_t& pow10)
+{
+ // LCOV_EXCL_START
+ if (n >= 1000000000)
+ {
+ pow10 = 1000000000;
+ return 10;
+ }
+ // LCOV_EXCL_STOP
+ else if (n >= 100000000)
+ {
+ pow10 = 100000000;
+ return 9;
+ }
+ else if (n >= 10000000)
+ {
+ pow10 = 10000000;
+ return 8;
+ }
+ else if (n >= 1000000)
+ {
+ pow10 = 1000000;
+ return 7;
+ }
+ else if (n >= 100000)
+ {
+ pow10 = 100000;
+ return 6;
+ }
+ else if (n >= 10000)
+ {
+ pow10 = 10000;
+ return 5;
+ }
+ else if (n >= 1000)
+ {
+ pow10 = 1000;
+ return 4;
+ }
+ else if (n >= 100)
+ {
+ pow10 = 100;
+ return 3;
+ }
+ else if (n >= 10)
+ {
+ pow10 = 10;
+ return 2;
+ }
+ else
+ {
+ pow10 = 1;
+ return 1;
+ }
+}
+
+inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta,
+ uint64_t rest, uint64_t ten_k)
+{
+ assert(len >= 1);
+ assert(dist <= delta);
+ assert(rest <= delta);
+ assert(ten_k > 0);
+
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // ten_k
+ // <------>
+ // <---- rest ---->
+ // --------------[------------------+----+--------------]--------------
+ // w V
+ // = buf * 10^k
+ //
+ // ten_k represents a unit-in-the-last-place in the decimal representation
+ // stored in buf.
+ // Decrement buf by ten_k while this takes buf closer to w.
+
+ // The tests are written in this order to avoid overflow in unsigned
+ // integer arithmetic.
+
+ while (rest < dist
+ and delta - rest >= ten_k
+ and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))
+ {
+ assert(buf[len - 1] != '0');
+ buf[len - 1]--;
+ rest += ten_k;
+ }
+}
+
+/*!
+Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
+M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
+*/
+inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
+ diyfp M_minus, diyfp w, diyfp M_plus)
+{
+ static_assert(kAlpha >= -60, "internal error");
+ static_assert(kGamma <= -32, "internal error");
+
+ // Generates the digits (and the exponent) of a decimal floating-point
+ // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
+ // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
+ //
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // Grisu2 generates the digits of M+ from left to right and stops as soon as
+ // V is in [M-,M+].
+
+ assert(M_plus.e >= kAlpha);
+ assert(M_plus.e <= kGamma);
+
+ uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
+ uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e)
+
+ // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
+ //
+ // M+ = f * 2^e
+ // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
+ // = ((p1 ) * 2^-e + (p2 )) * 2^e
+ // = p1 + p2 * 2^e
+
+ const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e);
+
+ uint32_t p1 = static_cast<uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+ uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e
+
+ // 1)
+ //
+ // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
+
+ assert(p1 > 0);
+
+ uint32_t pow10;
+ const int k = find_largest_pow10(p1, pow10);
+
+ // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
+ //
+ // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
+ // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1))
+ //
+ // M+ = p1 + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
+ // = d[k-1] * 10^(k-1) + ( rest) * 2^e
+ //
+ // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
+ //
+ // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
+ //
+ // but stop as soon as
+ //
+ // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
+
+ int n = k;
+ while (n > 0)
+ {
+ // Invariants:
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k)
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ //
+ const uint32_t d = p1 / pow10; // d = p1 div 10^(n-1)
+ const uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1)
+ //
+ // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
+ // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
+ //
+ assert(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
+ //
+ p1 = r;
+ n--;
+ //
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e)
+ // pow10 = 10^n
+ //
+
+ // Now check if enough digits have been generated.
+ // Compute
+ //
+ // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
+ //
+ // Note:
+ // Since rest and delta share the same exponent e, it suffices to
+ // compare the significands.
+ const uint64_t rest = (uint64_t{p1} << -one.e) + p2;
+ if (rest <= delta)
+ {
+ // V = buffer * 10^n, with M- <= V <= M+.
+
+ decimal_exponent += n;
+
+ // We may now just stop. But instead look if the buffer could be
+ // decremented to bring V closer to w.
+ //
+ // pow10 = 10^n is now 1 ulp in the decimal representation V.
+ // The rounding procedure works with diyfp's with an implicit
+ // exponent of e.
+ //
+ // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
+ //
+ const uint64_t ten_n = uint64_t{pow10} << -one.e;
+ grisu2_round(buffer, length, dist, delta, rest, ten_n);
+
+ return;
+ }
+
+ pow10 /= 10;
+ //
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ // Invariants restored.
+ }
+
+ // 2)
+ //
+ // The digits of the integral part have been generated:
+ //
+ // M+ = d[k-1]...d[1]d[0] + p2 * 2^e
+ // = buffer + p2 * 2^e
+ //
+ // Now generate the digits of the fractional part p2 * 2^e.
+ //
+ // Note:
+ // No decimal point is generated: the exponent is adjusted instead.
+ //
+ // p2 actually represents the fraction
+ //
+ // p2 * 2^e
+ // = p2 / 2^-e
+ // = d[-1] / 10^1 + d[-2] / 10^2 + ...
+ //
+ // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
+ //
+ // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
+ // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
+ //
+ // using
+ //
+ // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
+ // = ( d) * 2^-e + ( r)
+ //
+ // or
+ // 10^m * p2 * 2^e = d + r * 2^e
+ //
+ // i.e.
+ //
+ // M+ = buffer + p2 * 2^e
+ // = buffer + 10^-m * (d + r * 2^e)
+ // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
+ //
+ // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
+
+ assert(p2 > delta);
+
+ int m = 0;
+ for (;;)
+ {
+ // Invariant:
+ // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
+ // = buffer * 10^-m + 10^-m * (p2 ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
+ //
+ assert(p2 <= UINT64_MAX / 10);
+ p2 *= 10;
+ const uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e
+ const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
+ //
+ // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
+ // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ assert(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ p2 = r;
+ m++;
+ //
+ // M+ = buffer * 10^-m + 10^-m * p2 * 2^e
+ // Invariant restored.
+
+ // Check if enough digits have been generated.
+ //
+ // 10^-m * p2 * 2^e <= delta * 2^e
+ // p2 * 2^e <= 10^m * delta * 2^e
+ // p2 <= 10^m * delta
+ delta *= 10;
+ dist *= 10;
+ if (p2 <= delta)
+ {
+ break;
+ }
+ }
+
+ // V = buffer * 10^-m, with M- <= V <= M+.
+
+ decimal_exponent -= m;
+
+ // 1 ulp in the decimal representation is now 10^-m.
+ // Since delta and dist are now scaled by 10^m, we need to do the
+ // same with ulp in order to keep the units in sync.
+ //
+ // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
+ //
+ const uint64_t ten_m = one.f;
+ grisu2_round(buffer, length, dist, delta, p2, ten_m);
+
+ // By construction this algorithm generates the shortest possible decimal
+ // number (Loitsch, Theorem 6.2) which rounds back to w.
+ // For an input number of precision p, at least
+ //
+ // N = 1 + ceil(p * log_10(2))
+ //
+ // decimal digits are sufficient to identify all binary floating-point
+ // numbers (Matula, "In-and-Out conversions").
+ // This implies that the algorithm does not produce more than N decimal
+ // digits.
+ //
+ // N = 17 for p = 53 (IEEE double precision)
+ // N = 9 for p = 24 (IEEE single precision)
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+inline void grisu2(char* buf, int& len, int& decimal_exponent,
+ diyfp m_minus, diyfp v, diyfp m_plus)
+{
+ assert(m_plus.e == m_minus.e);
+ assert(m_plus.e == v.e);
+
+ // --------(-----------------------+-----------------------)-------- (A)
+ // m- v m+
+ //
+ // --------------------(-----------+-----------------------)-------- (B)
+ // m- v m+
+ //
+ // First scale v (and m- and m+) such that the exponent is in the range
+ // [alpha, gamma].
+
+ const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
+
+ const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
+
+ // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
+ const diyfp w = diyfp::mul(v, c_minus_k);
+ const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
+ const diyfp w_plus = diyfp::mul(m_plus, c_minus_k);
+
+ // ----(---+---)---------------(---+---)---------------(---+---)----
+ // w- w w+
+ // = c*m- = c*v = c*m+
+ //
+ // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
+ // w+ are now off by a small amount.
+ // In fact:
+ //
+ // w - v * 10^k < 1 ulp
+ //
+ // To account for this inaccuracy, add resp. subtract 1 ulp.
+ //
+ // --------+---[---------------(---+---)---------------]---+--------
+ // w- M- w M+ w+
+ //
+ // Now any number in [M-, M+] (bounds included) will round to w when input,
+ // regardless of how the input rounding algorithm breaks ties.
+ //
+ // And digit_gen generates the shortest possible such number in [M-, M+].
+ // Note that this does not mean that Grisu2 always generates the shortest
+ // possible number in the interval (m-, m+).
+ const diyfp M_minus(w_minus.f + 1, w_minus.e);
+ const diyfp M_plus (w_plus.f - 1, w_plus.e );
+
+ decimal_exponent = -cached.k; // = -(-k) = k
+
+ grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+template <typename FloatType>
+void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
+{
+ static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+ "internal error: not enough precision");
+
+ assert(std::isfinite(value));
+ assert(value > 0);
+
+ // If the neighbors (and boundaries) of 'value' are always computed for double-precision
+ // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
+ // decimal representations are not exactly "short".
+ //
+ // The documentation for 'std::to_chars' (http://en.cppreference.com/w/cpp/utility/to_chars)
+ // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
+ // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'
+ // does.
+ // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
+ // representation using the corresponding std::from_chars function recovers value exactly". That
+ // indicates that single precision floating-point numbers should be recovered using
+ // 'std::strtof'.
+ //
+ // NB: If the neighbors are computed for single-precision numbers, there is a single float
+ // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
+ // value is off by 1 ulp.
+#if 0
+ const boundaries w = compute_boundaries(static_cast<double>(value));
+#else
+ const boundaries w = compute_boundaries(value);
+#endif
+
+ grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+}
+
+/*!
+@brief appends a decimal representation of e to buf
+@return a pointer to the element following the exponent.
+@pre -1000 < e < 1000
+*/
+inline char* append_exponent(char* buf, int e)
+{
+ assert(e > -1000);
+ assert(e < 1000);
+
+ if (e < 0)
+ {
+ e = -e;
+ *buf++ = '-';
+ }
+ else
+ {
+ *buf++ = '+';
+ }
+
+ uint32_t k = static_cast<uint32_t>(e);
+ if (k < 10)
+ {
+ // Always print at least two digits in the exponent.
+ // This is for compatibility with printf("%g").
+ *buf++ = '0';
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else if (k < 100)
+ {
+ *buf++ = static_cast<char>('0' + k / 10);
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else
+ {
+ *buf++ = static_cast<char>('0' + k / 100);
+ k %= 100;
+ *buf++ = static_cast<char>('0' + k / 10);
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+
+ return buf;
+}
+
+/*!
+@brief prettify v = buf * 10^decimal_exponent
+
+If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
+notation. Otherwise it will be printed in exponential notation.
+
+@pre min_exp < 0
+@pre max_exp > 0
+*/
+inline char* format_buffer(char* buf, int len, int decimal_exponent,
+ int min_exp, int max_exp)
+{
+ assert(min_exp < 0);
+ assert(max_exp > 0);
+
+ const int k = len;
+ const int n = len + decimal_exponent;
+
+ // v = buf * 10^(n-k)
+ // k is the length of the buffer (number of decimal digits)
+ // n is the position of the decimal point relative to the start of the buffer.
+
+ if (k <= n and n <= max_exp)
+ {
+ // digits[000]
+ // len <= max_exp + 2
+
+ std::memset(buf + k, '0', static_cast<size_t>(n - k));
+ // Make it look like a floating-point number (#362, #378)
+ buf[n + 0] = '.';
+ buf[n + 1] = '0';
+ return buf + (n + 2);
+ }
+
+ if (0 < n and n <= max_exp)
+ {
+ // dig.its
+ // len <= max_digits10 + 1
+
+ assert(k > n);
+
+ std::memmove(buf + (n + 1), buf + n, static_cast<size_t>(k - n));
+ buf[n] = '.';
+ return buf + (k + 1);
+ }
+
+ if (min_exp < n and n <= 0)
+ {
+ // 0.[000]digits
+ // len <= 2 + (-min_exp - 1) + max_digits10
+
+ std::memmove(buf + (2 + -n), buf, static_cast<size_t>(k));
+ buf[0] = '0';
+ buf[1] = '.';
+ std::memset(buf + 2, '0', static_cast<size_t>(-n));
+ return buf + (2 + (-n) + k);
+ }
+
+ if (k == 1)
+ {
+ // dE+123
+ // len <= 1 + 5
+
+ buf += 1;
+ }
+ else
+ {
+ // d.igitsE+123
+ // len <= max_digits10 + 1 + 5
+
+ std::memmove(buf + 2, buf + 1, static_cast<size_t>(k - 1));
+ buf[1] = '.';
+ buf += 1 + k;
+ }
+
+ *buf++ = 'e';
+ return append_exponent(buf, n - 1);
+}
+
+} // namespace dtoa_impl
+
+/*!
+@brief generates a decimal representation of the floating-point number value in [first, last).
+
+The format of the resulting decimal representation is similar to printf's %g
+format. Returns an iterator pointing past-the-end of the decimal representation.
+
+@note The input number must be finite, i.e. NaN's and Inf's are not supported.
+@note The buffer must be large enough.
+@note The result is NOT null-terminated.
+*/
+template <typename FloatType>
+char* to_chars(char* first, char* last, FloatType value)
+{
+ static_cast<void>(last); // maybe unused - fix warning
+ assert(std::isfinite(value));
+
+ // Use signbit(value) instead of (value < 0) since signbit works for -0.
+ if (std::signbit(value))
+ {
+ value = -value;
+ *first++ = '-';
+ }
+
+ if (value == 0) // +-0
+ {
+ *first++ = '0';
+ // Make it look like a floating-point number (#362, #378)
+ *first++ = '.';
+ *first++ = '0';
+ return first;
+ }
+
+ assert(last - first >= std::numeric_limits<FloatType>::max_digits10);
+
+ // Compute v = buffer * 10^decimal_exponent.
+ // The decimal digits are stored in the buffer, which needs to be interpreted
+ // as an unsigned decimal integer.
+ // len is the length of the buffer, i.e. the number of decimal digits.
+ int len = 0;
+ int decimal_exponent = 0;
+ dtoa_impl::grisu2(first, len, decimal_exponent, value);
+
+ assert(len <= std::numeric_limits<FloatType>::max_digits10);
+
+ // Format the buffer like printf("%.*g", prec, value)
+ constexpr int kMinExp = -4;
+ // Use digits10 here to increase compatibility with version 2.
+ constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
+
+ assert(last - first >= kMaxExp + 2);
+ assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+ assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+
+ return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+
+} // namespace
+
+void json::serializer::dump(const json& val, const bool pretty_print,
+ const bool ensure_ascii,
+ const unsigned int indent_step,
+ const unsigned int current_indent)
+{
+ switch (val.m_type)
+ {
+ case value_t::object:
+ {
+ if (val.m_value.object->empty())
+ {
+ o << "{}";
+ return;
+ }
+
+ // we need to iterate over the object values in sorted key order
+ SmallVector<StringMapConstIterator<json>, 64> sorted;
+ for (auto i = val.m_value.object->begin(),
+ end = val.m_value.object->end(); i != end; ++i)
+ {
+ sorted.push_back(i);
+ }
+ std::sort(sorted.begin(), sorted.end(),
+ [](const StringMapConstIterator<json>& a,
+ const StringMapConstIterator<json>& b) {
+ return a->getKey() < b->getKey();
+ });
+
+ if (pretty_print)
+ {
+ o << "{\n";
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, indent_char);
+ }
+
+ // first n-1 elements
+ auto i = sorted.begin();
+ for (std::size_t cnt = 0; cnt < sorted.size() - 1; ++cnt, ++i)
+ {
+ o.write(indent_string.c_str(), new_indent);
+ o << '\"';
+ dump_escaped((*i)->first(), ensure_ascii);
+ o << "\": ";
+ dump((*i)->second, true, ensure_ascii, indent_step, new_indent);
+ o << ",\n";
+ }
+
+ // last element
+ assert(i != sorted.end());
+ //assert(std::next(i) == val.m_value.object->end());
+ o.write(indent_string.c_str(), new_indent);
+ o << '\"';
+ dump_escaped((*i)->first(), ensure_ascii);
+ o << "\": ";
+ dump((*i)->second, true, ensure_ascii, indent_step, new_indent);
+
+ o << '\n';
+ o.write(indent_string.c_str(), current_indent);
+ o << '}';
+ }
+ else
+ {
+ o << '{';
+
+ // first n-1 elements
+ auto i = sorted.begin();
+ for (std::size_t cnt = 0; cnt < sorted.size() - 1; ++cnt, ++i)
+ {
+ o << '\"';
+ dump_escaped((*i)->first(), ensure_ascii);
+ o << "\":";
+ dump((*i)->second, false, ensure_ascii, indent_step, current_indent);
+ o << ',';
+ }
+
+ // last element
+ assert(i != sorted.end());
+ //assert(std::next(i) == val.m_value.object->end());
+ o << '\"';
+ dump_escaped((*i)->first(), ensure_ascii);
+ o << "\":";
+ dump((*i)->second, false, ensure_ascii, indent_step, current_indent);
+
+ o << '}';
+ }
+
+ return;
+ }
+
+ case value_t::array:
+ {
+ if (val.m_value.array->empty())
+ {
+ o << "[]";
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o << "[\n";
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, indent_char);
+ }
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ o.write(indent_string.c_str(), new_indent);
+ dump(*i, true, ensure_ascii, indent_step, new_indent);
+ o << ",\n";
+ }
+
+ // last element
+ assert(not val.m_value.array->empty());
+ o.write(indent_string.c_str(), new_indent);
+ dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+ o << '\n';
+ o.write(indent_string.c_str(), current_indent);
+ o << ']';
+ }
+ else
+ {
+ o << '[';
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ dump(*i, false, ensure_ascii, indent_step, current_indent);
+ o << ',';
+ }
+
+ // last element
+ assert(not val.m_value.array->empty());
+ dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+ o << ']';
+ }
+
+ return;
+ }
+
+ case value_t::string:
+ {
+ o << '\"';
+ dump_escaped(*val.m_value.string, ensure_ascii);
+ o << '\"';
+ return;
+ }
+
+ case value_t::boolean:
+ {
+ if (val.m_value.boolean)
+ {
+ o << "true";
+ }
+ else
+ {
+ o << "false";
+ }
+ return;
+ }
+
+ case value_t::number_integer:
+ {
+ dump_integer(val.m_value.number_integer);
+ return;
+ }
+
+ case value_t::number_unsigned:
+ {
+ dump_integer(val.m_value.number_unsigned);
+ return;
+ }
+
+ case value_t::number_float:
+ {
+ dump_float(val.m_value.number_float);
+ return;
+ }
+
+ case value_t::discarded:
+ {
+ o << "<discarded>";
+ return;
+ }
+
+ case value_t::null:
+ {
+ o << "null";
+ return;
+ }
+ }
+}
+
+void json::serializer::dump_escaped(StringRef s, const bool ensure_ascii)
+{
+ uint32_t codepoint;
+ uint8_t state = UTF8_ACCEPT;
+
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ const auto byte = static_cast<uint8_t>(s[i]);
+
+ switch (decode(state, codepoint, byte))
+ {
+ case UTF8_ACCEPT: // decode found a new code point
+ {
+ switch (codepoint)
+ {
+ case 0x08: // backspace
+ {
+ o << '\\' << 'b';
+ break;
+ }
+
+ case 0x09: // horizontal tab
+ {
+ o << '\\' << 't';
+ break;
+ }
+
+ case 0x0A: // newline
+ {
+ o << '\\' << 'n';
+ break;
+ }
+
+ case 0x0C: // formfeed
+ {
+ o << '\\' << 'f';
+ break;
+ }
+
+ case 0x0D: // carriage return
+ {
+ o << '\\' << 'r';
+ break;
+ }
+
+ case 0x22: // quotation mark
+ {
+ o << '\\' << '\"';
+ break;
+ }
+
+ case 0x5C: // reverse solidus
+ {
+ o << '\\' << '\\';
+ break;
+ }
+
+ default:
+ {
+ // escape control characters (0x00..0x1F) or, if
+ // ensure_ascii parameter is used, non-ASCII characters
+ if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
+ {
+ if (codepoint <= 0xFFFF)
+ {
+ o << '\\' << 'u' << format_hex_no_prefix(codepoint, 4);
+ }
+ else
+ {
+ o << '\\' << 'u' << format_hex_no_prefix(0xD7C0 + (codepoint >> 10), 4);
+ o << '\\' << 'u' << format_hex_no_prefix(0xDC00 + (codepoint & 0x3FF), 4);
+ }
+ }
+ else
+ {
+ // copy byte to buffer (all previous bytes
+ // been copied have in default case above)
+ o << s[i];
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case UTF8_REJECT: // decode found invalid UTF-8 byte
+ {
+ JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + Twine(i) + ": 0x" + Twine::utohexstr(byte)));
+ }
+
+ default: // decode found yet incomplete multi-byte code point
+ {
+ if (not ensure_ascii)
+ {
+ // code point will not be escaped - copy byte to buffer
+ o << s[i];
+ }
+ break;
+ }
+ }
+ }
+
+ if (JSON_UNLIKELY(state != UTF8_ACCEPT))
+ {
+ // we finish reading, but do not accept: string was incomplete
+ JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + Twine::utohexstr(static_cast<uint8_t>(s.back()))));
+ }
+}
+
+void json::serializer::dump_float(double x)
+{
+ // NaN / inf
+ if (not std::isfinite(x))
+ {
+ o << "null";
+ return;
+ }
+
+ // use the Grisu2 algorithm to produce short numbers which are
+ // guaranteed to round-trip, using strtof and strtod, resp.
+ char* begin = number_buffer.data();
+ char* end = to_chars(begin, begin + number_buffer.size(), x);
+
+ o.write(begin, static_cast<size_t>(end - begin));
+}
+
+uint8_t json::serializer::decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept
+{
+ static const std::array<uint8_t, 400> utf8d =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+ 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
+ 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+ 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+ 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+ 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+ 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+ }
+ };
+
+ const uint8_t type = utf8d[byte];
+
+ codep = (state != UTF8_ACCEPT)
+ ? (byte & 0x3fu) | (codep << 6)
+ : static_cast<uint32_t>(0xff >> type) & (byte);
+
+ state = utf8d[256u + state * 16u + type];
+ return state;
+}
+
+std::string json::dump(const int indent, const char indent_char,
+ const bool ensure_ascii) const
+{
+ std::string result;
+ raw_string_ostream os(result);
+ dump(os, indent, indent_char, ensure_ascii);
+ return result;
+}
+
+void json::dump(raw_ostream& os, int indent, const char indent_char,
+ const bool ensure_ascii) const
+{
+ serializer s(os, indent_char);
+
+ if (indent >= 0)
+ {
+ s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
+ }
+ else
+ {
+ s.dump(*this, false, ensure_ascii, 0);
+ }
+
+ os.flush();
+}
+
+raw_ostream& operator<<(raw_ostream& o, const json& j)
+{
+ j.dump(o, 0);
+ return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const json& j)
+{
+ raw_os_ostream os(o);
+ j.dump(os, 0);
+ return o;
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/json_serializer.h b/wpiutil/src/main/native/cpp/json_serializer.h
new file mode 100644
index 0000000..6983920
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/json_serializer.h
@@ -0,0 +1,197 @@
+/*----------------------------------------------------------------------------*/
+/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+/*
+ __ _____ _____ _____
+ __| | __| | | | JSON for Modern C++
+| | |__ | | | | | | version 3.1.2
+|_____|_____|_____|_|___| https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#include "wpi/json.h"
+
+#include <clocale> // lconv, localeconv
+#include <cmath> // labs, isfinite, isnan, signbit, ldexp
+#include <locale> // locale
+
+#include "wpi/raw_ostream.h"
+
+namespace wpi {
+
+class json::serializer
+{
+ static constexpr uint8_t UTF8_ACCEPT = 0;
+ static constexpr uint8_t UTF8_REJECT = 1;
+
+ public:
+ /*!
+ @param[in] s output stream to serialize to
+ @param[in] ichar indentation character to use
+ */
+ serializer(raw_ostream& s, const char ichar)
+ : o(s), loc(std::localeconv()), indent_char(ichar),
+ indent_string(512, indent_char)
+ {}
+
+ // delete because of pointer members
+ serializer(const serializer&) = delete;
+ serializer& operator=(const serializer&) = delete;
+
+ /*!
+ @brief internal implementation of the serialization function
+
+ This function is called by the public member function dump and organizes
+ the serialization internally. The indentation level is propagated as
+ additional parameter. In case of arrays and objects, the function is
+ called recursively.
+
+ - strings and object keys are escaped using `escape_string()`
+ - integer numbers are converted implicitly via `operator<<`
+ - floating-point numbers are converted to a string using `"%g"` format
+
+ @param[in] val value to serialize
+ @param[in] pretty_print whether the output shall be pretty-printed
+ @param[in] indent_step the indent level
+ @param[in] current_indent the current indent level (only used internally)
+ */
+ void dump(const json& val, const bool pretty_print,
+ const bool ensure_ascii,
+ const unsigned int indent_step,
+ const unsigned int current_indent = 0);
+
+ private:
+ /*!
+ @brief dump escaped string
+
+ Escape a string by replacing certain special characters by a sequence of an
+ escape character (backslash) and another character and other control
+ characters by a sequence of "\u" followed by a four-digit hex
+ representation. The escaped string is written to output stream @a o.
+
+ @param[in] s the string to escape
+ @param[in] ensure_ascii whether to escape non-ASCII characters with
+ \uXXXX sequences
+
+ @complexity Linear in the length of string @a s.
+ */
+ void dump_escaped(StringRef s, const bool ensure_ascii);
+
+ /*!
+ @brief dump an integer
+
+ Dump a given integer to output stream @a o. Works internally with
+ @a number_buffer.
+
+ @param[in] x integer number (signed or unsigned) to dump
+ @tparam NumberType either @a int64_t or @a uint64_t
+ */
+ template<typename NumberType, detail::enable_if_t<
+ std::is_same<NumberType, uint64_t>::value or
+ std::is_same<NumberType, int64_t>::value,
+ int> = 0>
+ void dump_integer(NumberType x)
+ {
+ // special case for "0"
+ if (x == 0)
+ {
+ o << '0';
+ return;
+ }
+
+ const bool is_negative = (x <= 0) and (x != 0); // see issue #755
+ std::size_t i = 0;
+
+ while (x != 0)
+ {
+ // spare 1 byte for '\0'
+ assert(i < number_buffer.size() - 1);
+
+ const auto digit = std::labs(static_cast<long>(x % 10));
+ number_buffer[i++] = static_cast<char>('0' + digit);
+ x /= 10;
+ }
+
+ if (is_negative)
+ {
+ // make sure there is capacity for the '-'
+ assert(i < number_buffer.size() - 2);
+ number_buffer[i++] = '-';
+ }
+
+ std::reverse(number_buffer.begin(), number_buffer.begin() + i);
+ o.write(number_buffer.data(), i);
+ }
+
+ /*!
+ @brief dump a floating-point number
+
+ Dump a given floating-point number to output stream @a o. Works internally
+ with @a number_buffer.
+
+ @param[in] x floating-point number to dump
+ */
+ void dump_float(double x);
+
+ /*!
+ @brief check whether a string is UTF-8 encoded
+
+ The function checks each byte of a string whether it is UTF-8 encoded. The
+ result of the check is stored in the @a state parameter. The function must
+ be called initially with state 0 (accept). State 1 means the string must
+ be rejected, because the current byte is not allowed. If the string is
+ completely processed, but the state is non-zero, the string ended
+ prematurely; that is, the last byte indicated more bytes should have
+ followed.
+
+ @param[in,out] state the state of the decoding
+ @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
+ @param[in] byte next byte to decode
+ @return new state
+
+ @note The function has been edited: a std::array is used.
+
+ @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ */
+ static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept;
+
+ private:
+ /// the output of the serializer
+ raw_ostream& o;
+
+ /// a (hopefully) large enough character buffer
+ std::array<char, 64> number_buffer{{}};
+
+ /// the locale
+ const std::lconv* loc = nullptr;
+
+ /// the indentation character
+ const char indent_char;
+ /// the indentation string
+ std::string indent_string;
+};
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/leb128.cpp b/wpiutil/src/main/native/cpp/leb128.cpp
new file mode 100644
index 0000000..202ee9a
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/leb128.cpp
@@ -0,0 +1,81 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/leb128.h"
+
+#include "wpi/raw_istream.h"
+
+namespace wpi {
+
+uint64_t SizeUleb128(uint64_t val) {
+ size_t count = 0;
+ do {
+ val >>= 7;
+ ++count;
+ } while (val != 0);
+ return count;
+}
+
+uint64_t WriteUleb128(SmallVectorImpl<char>& dest, uint64_t val) {
+ size_t count = 0;
+
+ do {
+ unsigned char byte = val & 0x7f;
+ val >>= 7;
+
+ if (val != 0)
+ byte |= 0x80; // mark this byte to show that more bytes will follow
+
+ dest.push_back(byte);
+ count++;
+ } while (val != 0);
+
+ return count;
+}
+
+uint64_t ReadUleb128(const char* addr, uint64_t* ret) {
+ uint64_t result = 0;
+ int shift = 0;
+ size_t count = 0;
+
+ while (1) {
+ unsigned char byte = *reinterpret_cast<const unsigned char*>(addr);
+ addr++;
+ count++;
+
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+
+ if (!(byte & 0x80)) break;
+ }
+
+ *ret = result;
+
+ return count;
+}
+
+bool ReadUleb128(raw_istream& is, uint64_t* ret) {
+ uint64_t result = 0;
+ int shift = 0;
+
+ while (1) {
+ unsigned char byte;
+ is.read(reinterpret_cast<char*>(&byte), 1);
+ if (is.has_error()) return false;
+
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+
+ if (!(byte & 0x80)) break;
+ }
+
+ *ret = result;
+
+ return true;
+}
+
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
new file mode 100644
index 0000000..abe3744
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
@@ -0,0 +1,739 @@
+/*===--- ConvertUTF.c - Universal Character Names conversions ---------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is distributed under the University of Illinois Open Source
+ * License. See LICENSE.TXT for details.
+ *
+ *===------------------------------------------------------------------------=*/
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+#include "wpi/ConvertUTF.h"
+#ifdef CVTUTF_DEBUG
+#include <stdio.h>
+#endif
+#include <assert.h>
+
+/*
+ * This code extensively uses fall-through switches.
+ * Keep the compiler from warning about that.
+ */
+#if defined(__clang__) && defined(__has_warning)
+# if __has_warning("-Wimplicit-fallthrough")
+# define ConvertUTF_DISABLE_WARNINGS \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wimplicit-fallthrough\"")
+# define ConvertUTF_RESTORE_WARNINGS \
+ _Pragma("clang diagnostic pop")
+# endif
+#elif defined(__GNUC__) && __GNUC__ > 6
+# define ConvertUTF_DISABLE_WARNINGS \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
+# define ConvertUTF_RESTORE_WARNINGS \
+ _Pragma("GCC diagnostic pop")
+#endif
+#ifndef ConvertUTF_DISABLE_WARNINGS
+# define ConvertUTF_DISABLE_WARNINGS
+#endif
+#ifndef ConvertUTF_RESTORE_WARNINGS
+# define ConvertUTF_RESTORE_WARNINGS
+#endif
+
+ConvertUTF_DISABLE_WARNINGS
+
+namespace wpi {
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF16 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ if (target >= targetEnd) {
+ result = targetExhausted; break;
+ }
+ ch = *source++;
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_LEGAL_UTF32) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ --source; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF32 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF32* target = *targetStart;
+ UTF32 ch, ch2;
+ while (source < sourceEnd) {
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ if (target >= targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ *target++ = ch;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+#ifdef CVTUTF_DEBUG
+if (result == sourceIllegal) {
+ fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
+ fflush(stderr);
+}
+#endif
+ return result;
+}
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ UTF32 ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF8 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ ch = *source++;
+ if (flags == strictConversion ) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /*
+ * Figure out how many bytes the result will require. Turn any
+ * illegally large UTF32 things (> Plane 17) into replacement chars.
+ */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ result = sourceIllegal;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ --source; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return false;
+ /* Everything else falls through when "true"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xED: if (a > 0x9F) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+ int length = trailingBytesForUTF8[*source]+1;
+ if (length > sourceEnd - source) {
+ return false;
+ }
+ return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned
+findMaximalSubpartOfIllFormedUTF8Sequence(const UTF8 *source,
+ const UTF8 *sourceEnd) {
+ UTF8 b1, b2, b3;
+
+ assert(!isLegalUTF8Sequence(source, sourceEnd));
+
+ /*
+ * Unicode 6.3.0, D93b:
+ *
+ * Maximal subpart of an ill-formed subsequence: The longest code unit
+ * subsequence starting at an unconvertible offset that is either:
+ * a. the initial subsequence of a well-formed code unit sequence, or
+ * b. a subsequence of length one.
+ */
+
+ if (source == sourceEnd)
+ return 0;
+
+ /*
+ * Perform case analysis. See Unicode 6.3.0, Table 3-7. Well-Formed UTF-8
+ * Byte Sequences.
+ */
+
+ b1 = *source;
+ ++source;
+ if (b1 >= 0xC2 && b1 <= 0xDF) {
+ /*
+ * First byte is valid, but we know that this code unit sequence is
+ * invalid, so the maximal subpart has to end after the first byte.
+ */
+ return 1;
+ }
+
+ if (source == sourceEnd)
+ return 1;
+
+ b2 = *source;
+ ++source;
+
+ if (b1 == 0xE0) {
+ return (b2 >= 0xA0 && b2 <= 0xBF) ? 2 : 1;
+ }
+ if (b1 >= 0xE1 && b1 <= 0xEC) {
+ return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
+ }
+ if (b1 == 0xED) {
+ return (b2 >= 0x80 && b2 <= 0x9F) ? 2 : 1;
+ }
+ if (b1 >= 0xEE && b1 <= 0xEF) {
+ return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
+ }
+ if (b1 == 0xF0) {
+ if (b2 >= 0x90 && b2 <= 0xBF) {
+ if (source == sourceEnd)
+ return 2;
+
+ b3 = *source;
+ return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
+ }
+ return 1;
+ }
+ if (b1 >= 0xF1 && b1 <= 0xF3) {
+ if (b2 >= 0x80 && b2 <= 0xBF) {
+ if (source == sourceEnd)
+ return 2;
+
+ b3 = *source;
+ return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
+ }
+ return 1;
+ }
+ if (b1 == 0xF4) {
+ if (b2 >= 0x80 && b2 <= 0x8F) {
+ if (source == sourceEnd)
+ return 2;
+
+ b3 = *source;
+ return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
+ }
+ return 1;
+ }
+
+ assert((b1 >= 0x80 && b1 <= 0xC1) || b1 >= 0xF5);
+ /*
+ * There are no valid sequences that start with these bytes. Maximal subpart
+ * is defined to have length 1 in these cases.
+ */
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return the total number of bytes in a codepoint
+ * represented in UTF-8, given the value of the first byte.
+ */
+unsigned getNumBytesForUTF8(UTF8 first) {
+ return trailingBytesForUTF8[first] + 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 string is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd) {
+ while (*source != sourceEnd) {
+ int length = trailingBytesForUTF8[**source] + 1;
+ if (length > sourceEnd - *source || !isLegalUTF8(*source, length))
+ return false;
+ *source += length;
+ }
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (extraBytesToRead >= sourceEnd - source) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (!isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ConversionResult ConvertUTF8toUTF32Impl(
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags,
+ Boolean InputIsPartial) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF32* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (extraBytesToRead >= sourceEnd - source) {
+ if (flags == strictConversion || InputIsPartial) {
+ result = sourceExhausted;
+ break;
+ } else {
+ result = sourceIllegal;
+
+ /*
+ * Replace the maximal subpart of ill-formed sequence with
+ * replacement character.
+ */
+ source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
+ sourceEnd);
+ *target++ = UNI_REPLACEMENT_CHAR;
+ continue;
+ }
+ }
+ if (target >= targetEnd) {
+ result = targetExhausted; break;
+ }
+
+ /* Do this check whether lenient or strict */
+ if (!isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ if (flags == strictConversion) {
+ /* Abort conversion. */
+ break;
+ } else {
+ /*
+ * Replace the maximal subpart of ill-formed sequence with
+ * replacement character.
+ */
+ source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
+ sourceEnd);
+ *target++ = UNI_REPLACEMENT_CHAR;
+ continue;
+ }
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6;
+ case 4: ch += *source++; ch <<= 6;
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (ch <= UNI_MAX_LEGAL_UTF32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = ch;
+ }
+ } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
+ result = sourceIllegal;
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+ConversionResult ConvertUTF8toUTF32Partial(const UTF8 **sourceStart,
+ const UTF8 *sourceEnd,
+ UTF32 **targetStart,
+ UTF32 *targetEnd,
+ ConversionFlags flags) {
+ return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
+ flags, /*InputIsPartial=*/true);
+}
+
+ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
+ const UTF8 *sourceEnd, UTF32 **targetStart,
+ UTF32 *targetEnd, ConversionFlags flags) {
+ return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
+ flags, /*InputIsPartial=*/false);
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
+
+} // namespace llvm
+
+ConvertUTF_RESTORE_WARNINGS
diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
new file mode 100644
index 0000000..3402988
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
@@ -0,0 +1,122 @@
+//===-- ConvertUTFWrapper.cpp - Wrap ConvertUTF.h with clang data types -----===
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/ConvertUTF.h"
+#include <string>
+#include <vector>
+
+namespace wpi {
+
+bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) {
+ const UTF32 *SourceStart = &Source;
+ const UTF32 *SourceEnd = SourceStart + 1;
+ UTF8 *TargetStart = reinterpret_cast<UTF8 *>(ResultPtr);
+ UTF8 *TargetEnd = TargetStart + 4;
+ ConversionResult CR = ConvertUTF32toUTF8(&SourceStart, SourceEnd,
+ &TargetStart, TargetEnd,
+ strictConversion);
+ if (CR != conversionOK)
+ return false;
+
+ ResultPtr = reinterpret_cast<char*>(TargetStart);
+ return true;
+}
+
+bool hasUTF16ByteOrderMark(ArrayRef<char> S) {
+ return (S.size() >= 2 &&
+ ((S[0] == '\xff' && S[1] == '\xfe') ||
+ (S[0] == '\xfe' && S[1] == '\xff')));
+}
+
+bool convertUTF16ToUTF8String(ArrayRef<UTF16> SrcUTF16,
+ SmallVectorImpl<char> &DstUTF8) {
+ assert(DstUTF8.empty());
+
+ // Avoid OOB by returning early on empty input.
+ if (SrcUTF16.empty())
+ return true;
+
+ const UTF16 *Src = SrcUTF16.begin();
+ const UTF16 *SrcEnd = SrcUTF16.end();
+
+ // Byteswap if necessary.
+ std::vector<UTF16> ByteSwapped;
+ if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_SWAPPED) {
+ ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd);
+ for (unsigned I = 0, E = ByteSwapped.size(); I != E; ++I)
+ ByteSwapped[I] = (ByteSwapped[I] << 8) | (ByteSwapped[I] >> 8);
+ Src = &ByteSwapped[0];
+ SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1;
+ }
+
+ // Skip the BOM for conversion.
+ if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_NATIVE)
+ Src++;
+
+ // Just allocate enough space up front. We'll shrink it later. Allocate
+ // enough that we can fit a null terminator without reallocating.
+ DstUTF8.resize(SrcUTF16.size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1);
+ UTF8 *Dst = reinterpret_cast<UTF8*>(&DstUTF8[0]);
+ UTF8 *DstEnd = Dst + DstUTF8.size();
+
+ ConversionResult CR =
+ ConvertUTF16toUTF8(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
+ assert(CR != targetExhausted);
+
+ if (CR != conversionOK) {
+ DstUTF8.clear();
+ return false;
+ }
+
+ DstUTF8.resize(reinterpret_cast<char*>(Dst) - &DstUTF8[0]);
+ DstUTF8.push_back(0);
+ DstUTF8.pop_back();
+ return true;
+}
+
+bool convertUTF8ToUTF16String(StringRef SrcUTF8,
+ SmallVectorImpl<UTF16> &DstUTF16) {
+ assert(DstUTF16.empty());
+
+ // Avoid OOB by returning early on empty input.
+ if (SrcUTF8.empty()) {
+ DstUTF16.push_back(0);
+ DstUTF16.pop_back();
+ return true;
+ }
+
+ const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.begin());
+ const UTF8 *SrcEnd = reinterpret_cast<const UTF8 *>(SrcUTF8.end());
+
+ // Allocate the same number of UTF-16 code units as UTF-8 code units. Encoding
+ // as UTF-16 should always require the same amount or less code units than the
+ // UTF-8 encoding. Allocate one extra byte for the null terminator though,
+ // so that someone calling DstUTF16.data() gets a null terminated string.
+ // We resize down later so we don't have to worry that this over allocates.
+ DstUTF16.resize(SrcUTF8.size()+1);
+ UTF16 *Dst = &DstUTF16[0];
+ UTF16 *DstEnd = Dst + DstUTF16.size();
+
+ ConversionResult CR =
+ ConvertUTF8toUTF16(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
+ assert(CR != targetExhausted);
+
+ if (CR != conversionOK) {
+ DstUTF16.clear();
+ return false;
+ }
+
+ DstUTF16.resize(Dst - &DstUTF16[0]);
+ DstUTF16.push_back(0);
+ DstUTF16.pop_back();
+ return true;
+}
+
+} // end namespace wpi
+
diff --git a/wpiutil/src/main/native/cpp/llvm/Error.cpp b/wpiutil/src/main/native/cpp/llvm/Error.cpp
new file mode 100644
index 0000000..7bf9c5f
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/Error.cpp
@@ -0,0 +1,138 @@
+//===----- lib/Support/Error.cpp - Error and associated utilities ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/Error.h"
+#include "wpi/Twine.h"
+#include "wpi/ErrorHandling.h"
+#include "wpi/ManagedStatic.h"
+#include <system_error>
+
+using namespace wpi;
+
+namespace {
+
+ enum class ErrorErrorCode : int {
+ MultipleErrors = 1,
+ FileError,
+ InconvertibleError
+ };
+
+ // FIXME: This class is only here to support the transition to wpi::Error. It
+ // will be removed once this transition is complete. Clients should prefer to
+ // deal with the Error value directly, rather than converting to error_code.
+ class ErrorErrorCategory : public std::error_category {
+ public:
+ const char *name() const noexcept override { return "Error"; }
+
+ std::string message(int condition) const override {
+ switch (static_cast<ErrorErrorCode>(condition)) {
+ case ErrorErrorCode::MultipleErrors:
+ return "Multiple errors";
+ case ErrorErrorCode::InconvertibleError:
+ return "Inconvertible error value. An error has occurred that could "
+ "not be converted to a known std::error_code. Please file a "
+ "bug.";
+ case ErrorErrorCode::FileError:
+ return "A file error occurred.";
+ }
+ wpi_unreachable("Unhandled error code");
+ }
+ };
+
+}
+
+static ManagedStatic<ErrorErrorCategory> ErrorErrorCat;
+
+namespace wpi {
+
+void ErrorInfoBase::anchor() {}
+char ErrorInfoBase::ID = 0;
+char ErrorList::ID = 0;
+void ECError::anchor() {}
+char ECError::ID = 0;
+char StringError::ID = 0;
+char FileError::ID = 0;
+
+void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) {
+ if (!E)
+ return;
+ OS << ErrorBanner;
+ handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
+ EI.log(OS);
+ OS << "\n";
+ });
+}
+
+
+std::error_code ErrorList::convertToErrorCode() const {
+ return std::error_code(static_cast<int>(ErrorErrorCode::MultipleErrors),
+ *ErrorErrorCat);
+}
+
+std::error_code inconvertibleErrorCode() {
+ return std::error_code(static_cast<int>(ErrorErrorCode::InconvertibleError),
+ *ErrorErrorCat);
+}
+
+std::error_code FileError::convertToErrorCode() const {
+ return std::error_code(static_cast<int>(ErrorErrorCode::FileError),
+ *ErrorErrorCat);
+}
+
+Error errorCodeToError(std::error_code EC) {
+ if (!EC)
+ return Error::success();
+ return Error(std::make_unique<ECError>(ECError(EC)));
+}
+
+std::error_code errorToErrorCode(Error Err) {
+ std::error_code EC;
+ handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) {
+ EC = EI.convertToErrorCode();
+ });
+ if (EC == inconvertibleErrorCode())
+ report_fatal_error(EC.message());
+ return EC;
+}
+
+StringError::StringError(std::error_code EC, const Twine &S)
+ : Msg(S.str()), EC(EC) {}
+
+StringError::StringError(const Twine &S, std::error_code EC)
+ : Msg(S.str()), EC(EC), PrintMsgOnly(true) {}
+
+void StringError::log(raw_ostream &OS) const {
+ if (PrintMsgOnly) {
+ OS << Msg;
+ } else {
+ OS << EC.message();
+ if (!Msg.empty())
+ OS << (" " + Msg);
+ }
+}
+
+std::error_code StringError::convertToErrorCode() const {
+ return EC;
+}
+
+Error createStringError(std::error_code EC, char const *Msg) {
+ return make_error<StringError>(Msg, EC);
+}
+
+void report_fatal_error(Error Err, bool GenCrashDiag) {
+ assert(Err && "report_fatal_error called with success value");
+ std::string ErrMsg;
+ {
+ raw_string_ostream ErrStream(ErrMsg);
+ logAllUnhandledErrors(std::move(Err), ErrStream);
+ }
+ report_fatal_error(ErrMsg);
+}
+
+} // end namespace wpi
diff --git a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
new file mode 100644
index 0000000..ef79456
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
@@ -0,0 +1,267 @@
+//===- lib/Support/ErrorHandling.cpp - Callbacks for errors ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines an API used to indicate fatal error conditions. Non-fatal
+// errors (most of them) should be handled through LLVMContext.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/ErrorHandling.h"
+#include "wpi/SmallVector.h"
+#include "wpi/Twine.h"
+#include "wpi/Error.h"
+#include "wpi/WindowsError.h"
+#include "wpi/raw_ostream.h"
+#include <cassert>
+#include <cstdlib>
+#include <mutex>
+#include <new>
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#if defined(_MSC_VER)
+#include <io.h>
+#endif
+
+using namespace wpi;
+
+static fatal_error_handler_t ErrorHandler = nullptr;
+static void *ErrorHandlerUserData = nullptr;
+
+static fatal_error_handler_t BadAllocErrorHandler = nullptr;
+static void *BadAllocErrorHandlerUserData = nullptr;
+
+// Mutexes to synchronize installing error handlers and calling error handlers.
+// Do not use ManagedStatic, or that may allocate memory while attempting to
+// report an OOM.
+//
+// This usage of std::mutex has to be conditionalized behind ifdefs because
+// of this script:
+// compiler-rt/lib/sanitizer_common/symbolizer/scripts/build_symbolizer.sh
+// That script attempts to statically link the LLVM symbolizer library with the
+// STL and hide all of its symbols with 'opt -internalize'. To reduce size, it
+// cuts out the threading portions of the hermetic copy of libc++ that it
+// builds. We can remove these ifdefs if that script goes away.
+static std::mutex ErrorHandlerMutex;
+static std::mutex BadAllocErrorHandlerMutex;
+
+void wpi::install_fatal_error_handler(fatal_error_handler_t handler,
+ void *user_data) {
+ std::scoped_lock Lock(ErrorHandlerMutex);
+ assert(!ErrorHandler && "Error handler already registered!\n");
+ ErrorHandler = handler;
+ ErrorHandlerUserData = user_data;
+}
+
+void wpi::remove_fatal_error_handler() {
+ std::scoped_lock Lock(ErrorHandlerMutex);
+ ErrorHandler = nullptr;
+ ErrorHandlerUserData = nullptr;
+}
+
+void wpi::report_fatal_error(const char *Reason, bool GenCrashDiag) {
+ report_fatal_error(Twine(Reason), GenCrashDiag);
+}
+
+void wpi::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
+ report_fatal_error(Twine(Reason), GenCrashDiag);
+}
+
+void wpi::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
+ report_fatal_error(Twine(Reason), GenCrashDiag);
+}
+
+void wpi::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
+ wpi::fatal_error_handler_t handler = nullptr;
+ void* handlerData = nullptr;
+ {
+ // Only acquire the mutex while reading the handler, so as not to invoke a
+ // user-supplied callback under a lock.
+ std::scoped_lock Lock(ErrorHandlerMutex);
+ handler = ErrorHandler;
+ handlerData = ErrorHandlerUserData;
+ }
+
+ if (handler) {
+ handler(handlerData, Reason.str(), GenCrashDiag);
+ } else {
+ // Blast the result out to stderr. We don't try hard to make sure this
+ // succeeds (e.g. handling EINTR) and we can't use errs() here because
+ // raw ostreams can call report_fatal_error.
+ SmallVector<char, 64> Buffer;
+ raw_svector_ostream OS(Buffer);
+ OS << "LLVM ERROR: " << Reason << "\n";
+ StringRef MessageStr = OS.str();
+#ifdef _WIN32
+ int written = ::_write(2, MessageStr.data(), MessageStr.size());
+#else
+ ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
+#endif
+ (void)written; // If something went wrong, we deliberately just give up.
+ }
+
+ exit(1);
+}
+
+void wpi::install_bad_alloc_error_handler(fatal_error_handler_t handler,
+ void *user_data) {
+ std::scoped_lock Lock(BadAllocErrorHandlerMutex);
+ assert(!ErrorHandler && "Bad alloc error handler already registered!\n");
+ BadAllocErrorHandler = handler;
+ BadAllocErrorHandlerUserData = user_data;
+}
+
+void wpi::remove_bad_alloc_error_handler() {
+ std::scoped_lock Lock(BadAllocErrorHandlerMutex);
+ BadAllocErrorHandler = nullptr;
+ BadAllocErrorHandlerUserData = nullptr;
+}
+
+void wpi::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
+ fatal_error_handler_t Handler = nullptr;
+ void *HandlerData = nullptr;
+ {
+ // Only acquire the mutex while reading the handler, so as not to invoke a
+ // user-supplied callback under a lock.
+ std::scoped_lock Lock(BadAllocErrorHandlerMutex);
+ Handler = BadAllocErrorHandler;
+ HandlerData = BadAllocErrorHandlerUserData;
+ }
+
+ if (Handler) {
+ Handler(HandlerData, Reason, GenCrashDiag);
+ wpi_unreachable("bad alloc handler should not return");
+ }
+
+ // Don't call the normal error handler. It may allocate memory. Directly write
+ // an OOM to stderr and abort.
+ char OOMMessage[] = "LLVM ERROR: out of memory\n";
+#ifdef _WIN32
+ int written = ::_write(2, OOMMessage, strlen(OOMMessage));
+#else
+ ssize_t written = ::write(2, OOMMessage, strlen(OOMMessage));
+#endif
+ (void)written;
+ abort();
+}
+
+// Causes crash on allocation failure. It is called prior to the handler set by
+// 'install_bad_alloc_error_handler'.
+static void out_of_memory_new_handler() {
+ wpi::report_bad_alloc_error("Allocation failed");
+}
+
+// Installs new handler that causes crash on allocation failure. It does not
+// need to be called explicitly, if this file is linked to application, because
+// in this case it is called during construction of 'new_handler_installer'.
+void wpi::install_out_of_memory_new_handler() {
+ static bool out_of_memory_new_handler_installed = false;
+ if (!out_of_memory_new_handler_installed) {
+ std::set_new_handler(out_of_memory_new_handler);
+ out_of_memory_new_handler_installed = true;
+ }
+}
+
+// Static object that causes installation of 'out_of_memory_new_handler' before
+// execution of 'main'.
+static class NewHandlerInstaller {
+public:
+ NewHandlerInstaller() {
+ install_out_of_memory_new_handler();
+ }
+} new_handler_installer;
+
+void wpi::wpi_unreachable_internal(const char *msg, const char *file,
+ unsigned line) {
+ // This code intentionally doesn't call the ErrorHandler callback, because
+ // wpi_unreachable is intended to be used to indicate "impossible"
+ // situations, and not legitimate runtime errors.
+ if (msg)
+ errs() << msg << "\n";
+ errs() << "UNREACHABLE executed";
+ if (file)
+ errs() << " at " << file << ":" << line;
+ errs() << "!\n";
+ abort();
+#ifdef LLVM_BUILTIN_UNREACHABLE
+ // Windows systems and possibly others don't declare abort() to be noreturn,
+ // so use the unreachable builtin to avoid a Clang self-host warning.
+ LLVM_BUILTIN_UNREACHABLE;
+#endif
+}
+
+#ifdef _WIN32
+
+#include <system_error>
+#include <winerror.h>
+
+// I'd rather not double the line count of the following.
+#define MAP_ERR_TO_COND(x, y) \
+ case x: \
+ return std::make_error_code(std::errc::y)
+
+std::error_code wpi::mapWindowsError(unsigned EV) {
+ switch (EV) {
+ MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
+ MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
+ MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
+ MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
+ MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
+ MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
+ MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
+ MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
+ MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
+ MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
+ MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
+ MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
+ MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
+ MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
+ MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
+ MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
+ MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
+ MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
+ MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
+ MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
+ MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
+ MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
+ MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
+ MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
+ MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
+ MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
+ MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
+ MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
+ MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
+ MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
+ MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
+ MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
+ MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
+ MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
+ MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
+ MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
+ MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
+ MAP_ERR_TO_COND(ERROR_SEEK, io_error);
+ MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
+ MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
+ MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
+ MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
+ MAP_ERR_TO_COND(WSAEACCES, permission_denied);
+ MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
+ MAP_ERR_TO_COND(WSAEFAULT, bad_address);
+ MAP_ERR_TO_COND(WSAEINTR, interrupted);
+ MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
+ MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
+ MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
+ default:
+ return std::error_code(EV, std::system_category());
+ }
+}
+
+#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/Hashing.cpp b/wpiutil/src/main/native/cpp/llvm/Hashing.cpp
new file mode 100644
index 0000000..e916751
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/Hashing.cpp
@@ -0,0 +1,29 @@
+//===-------------- lib/Support/Hashing.cpp -------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides implementation bits for the LLVM common hashing
+// infrastructure. Documentation and most of the other information is in the
+// header file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/Hashing.h"
+
+using namespace wpi;
+
+// Provide a definition and static initializer for the fixed seed. This
+// initializer should always be zero to ensure its value can never appear to be
+// non-zero, even during dynamic initialization.
+uint64_t wpi::hashing::detail::fixed_seed_override = 0;
+
+// Implement the function for forced setting of the fixed seed.
+// FIXME: Use atomic operations here so that there is no data race.
+void wpi::set_fixed_execution_hash_seed(uint64_t fixed_value) {
+ hashing::detail::fixed_seed_override = fixed_value;
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp b/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp
new file mode 100644
index 0000000..a8c82bf
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp
@@ -0,0 +1,88 @@
+//===-- ManagedStatic.cpp - Static Global wrapper -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the ManagedStatic class and wpi_shutdown().
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/ManagedStatic.h"
+#include "wpi/mutex.h"
+#include <cassert>
+#include <mutex>
+using namespace wpi;
+
+static const ManagedStaticBase *StaticList = nullptr;
+static wpi::mutex *ManagedStaticMutex = nullptr;
+static std::once_flag mutex_init_flag;
+
+static void initializeMutex() {
+ ManagedStaticMutex = new wpi::mutex();
+}
+
+static wpi::mutex* getManagedStaticMutex() {
+ std::call_once(mutex_init_flag, initializeMutex);
+ return ManagedStaticMutex;
+}
+
+void ManagedStaticBase::RegisterManagedStatic(void* created,
+ void (*Deleter)(void*)) const {
+ std::scoped_lock Lock(*getManagedStaticMutex());
+
+ if (!Ptr.load(std::memory_order_relaxed)) {
+ void *Tmp = created;
+
+ Ptr.store(Tmp, std::memory_order_release);
+ DeleterFn = Deleter;
+
+ // Add to list of managed statics.
+ Next = StaticList;
+ StaticList = this;
+ }
+}
+
+void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
+ void (*Deleter)(void*)) const {
+ assert(Creator);
+ std::scoped_lock Lock(*getManagedStaticMutex());
+
+ if (!Ptr.load(std::memory_order_relaxed)) {
+ void *Tmp = Creator();
+
+ Ptr.store(Tmp, std::memory_order_release);
+ DeleterFn = Deleter;
+
+ // Add to list of managed statics.
+ Next = StaticList;
+ StaticList = this;
+ }
+}
+
+void ManagedStaticBase::destroy() const {
+ assert(DeleterFn && "ManagedStatic not initialized correctly!");
+ assert(StaticList == this &&
+ "Not destroyed in reverse order of construction?");
+ // Unlink from list.
+ StaticList = Next;
+ Next = nullptr;
+
+ // Destroy memory.
+ DeleterFn(Ptr);
+
+ // Cleanup.
+ Ptr = nullptr;
+ DeleterFn = nullptr;
+}
+
+/// wpi_shutdown - Deallocate and destroy all ManagedStatic variables.
+void wpi::wpi_shutdown() {
+ std::scoped_lock Lock(*getManagedStaticMutex());
+
+ while (StaticList)
+ StaticList->destroy();
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp b/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp
new file mode 100644
index 0000000..985e269
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp
@@ -0,0 +1,264 @@
+//===- NativeFormatting.cpp - Low level formatting helpers -------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/NativeFormatting.h"
+
+#include "wpi/ArrayRef.h"
+#include "wpi/SmallString.h"
+#include "wpi/StringExtras.h"
+#include "wpi/Format.h"
+
+#include <float.h>
+
+using namespace wpi;
+
+template<typename T, std::size_t N>
+static int format_to_buffer(T Value, char (&Buffer)[N]) {
+ char *EndPtr = std::end(Buffer);
+ char *CurPtr = EndPtr;
+
+ do {
+ *--CurPtr = '0' + char(Value % 10);
+ Value /= 10;
+ } while (Value);
+ return EndPtr - CurPtr;
+}
+
+static void writeWithCommas(raw_ostream &S, ArrayRef<char> Buffer) {
+ assert(!Buffer.empty());
+
+ ArrayRef<char> ThisGroup;
+ int InitialDigits = ((Buffer.size() - 1) % 3) + 1;
+ ThisGroup = Buffer.take_front(InitialDigits);
+ S.write(ThisGroup.data(), ThisGroup.size());
+
+ Buffer = Buffer.drop_front(InitialDigits);
+ assert(Buffer.size() % 3 == 0);
+ while (!Buffer.empty()) {
+ S << ',';
+ ThisGroup = Buffer.take_front(3);
+ S.write(ThisGroup.data(), 3);
+ Buffer = Buffer.drop_front(3);
+ }
+}
+
+template <typename T>
+static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits,
+ IntegerStyle Style, bool IsNegative) {
+ static_assert(std::is_unsigned<T>::value, "Value is not unsigned!");
+
+ char NumberBuffer[128];
+ std::memset(NumberBuffer, '0', sizeof(NumberBuffer));
+
+ size_t Len = 0;
+ Len = format_to_buffer(N, NumberBuffer);
+
+ if (IsNegative)
+ S << '-';
+
+ if (Len < MinDigits && Style != IntegerStyle::Number) {
+ for (size_t I = Len; I < MinDigits; ++I)
+ S << '0';
+ }
+
+ if (Style == IntegerStyle::Number) {
+ writeWithCommas(S, ArrayRef<char>(std::end(NumberBuffer) - Len, Len));
+ } else {
+ S.write(std::end(NumberBuffer) - Len, Len);
+ }
+}
+
+template <typename T>
+static void write_unsigned(raw_ostream &S, T N, size_t MinDigits,
+ IntegerStyle Style, bool IsNegative = false) {
+ // Output using 32-bit div/mod if possible.
+ if (N == static_cast<uint32_t>(N))
+ write_unsigned_impl(S, static_cast<uint32_t>(N), MinDigits, Style,
+ IsNegative);
+ else
+ write_unsigned_impl(S, N, MinDigits, Style, IsNegative);
+}
+
+template <typename T>
+static void write_signed(raw_ostream &S, T N, size_t MinDigits,
+ IntegerStyle Style) {
+ static_assert(std::is_signed<T>::value, "Value is not signed!");
+
+ using UnsignedT = typename std::make_unsigned<T>::type;
+
+ if (N >= 0) {
+ write_unsigned(S, static_cast<UnsignedT>(N), MinDigits, Style);
+ return;
+ }
+
+ UnsignedT UN = -(UnsignedT)N;
+ write_unsigned(S, UN, MinDigits, Style, true);
+}
+
+void wpi::write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
+ IntegerStyle Style) {
+ write_unsigned(S, N, MinDigits, Style);
+}
+
+void wpi::write_integer(raw_ostream &S, int N, size_t MinDigits,
+ IntegerStyle Style) {
+ write_signed(S, N, MinDigits, Style);
+}
+
+void wpi::write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
+ IntegerStyle Style) {
+ write_unsigned(S, N, MinDigits, Style);
+}
+
+void wpi::write_integer(raw_ostream &S, long N, size_t MinDigits,
+ IntegerStyle Style) {
+ write_signed(S, N, MinDigits, Style);
+}
+
+void wpi::write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
+ IntegerStyle Style) {
+ write_unsigned(S, N, MinDigits, Style);
+}
+
+void wpi::write_integer(raw_ostream &S, long long N, size_t MinDigits,
+ IntegerStyle Style) {
+ write_signed(S, N, MinDigits, Style);
+}
+
+void wpi::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
+ std::optional<size_t> Width) {
+ const size_t kMaxWidth = 128u;
+
+ size_t W = std::min(kMaxWidth, Width.value_or(0u));
+
+ unsigned Nibbles = (64 - countLeadingZeros(N) + 3) / 4;
+ bool Prefix = (Style == HexPrintStyle::PrefixLower ||
+ Style == HexPrintStyle::PrefixUpper);
+ bool Upper =
+ (Style == HexPrintStyle::Upper || Style == HexPrintStyle::PrefixUpper);
+ unsigned PrefixChars = Prefix ? 2 : 0;
+ unsigned NumChars =
+ std::max(static_cast<unsigned>(W), std::max(1u, Nibbles) + PrefixChars);
+
+ char NumberBuffer[kMaxWidth];
+ ::memset(NumberBuffer, '0', wpi::array_lengthof(NumberBuffer));
+ if (Prefix)
+ NumberBuffer[1] = 'x';
+ char *EndPtr = NumberBuffer + NumChars;
+ char *CurPtr = EndPtr;
+ while (N) {
+ unsigned char x = static_cast<unsigned char>(N) % 16;
+ *--CurPtr = hexdigit(x, !Upper);
+ N /= 16;
+ }
+
+ S.write(NumberBuffer, NumChars);
+}
+
+void wpi::write_double(raw_ostream &S, double N, FloatStyle Style,
+ std::optional<size_t> Precision) {
+ size_t Prec = Precision.value_or(getDefaultPrecision(Style));
+
+ if (std::isnan(N)) {
+ S << "nan";
+ return;
+ } else if (std::isinf(N)) {
+ S << "INF";
+ return;
+ }
+
+ char Letter;
+ if (Style == FloatStyle::Exponent)
+ Letter = 'e';
+ else if (Style == FloatStyle::ExponentUpper)
+ Letter = 'E';
+ else
+ Letter = 'f';
+
+ SmallString<8> Spec;
+ wpi::raw_svector_ostream Out(Spec);
+ Out << "%." << Prec << Letter;
+
+ if (Style == FloatStyle::Exponent || Style == FloatStyle::ExponentUpper) {
+#ifdef _WIN32
+// On MSVCRT and compatible, output of %e is incompatible to Posix
+// by default. Number of exponent digits should be at least 2. "%+03d"
+// FIXME: Implement our formatter to here or Support/Format.h!
+#if defined(__MINGW32__)
+ // FIXME: It should be generic to C++11.
+ if (N == 0.0 && std::signbit(N)) {
+ char NegativeZero[] = "-0.000000e+00";
+ if (Style == FloatStyle::ExponentUpper)
+ NegativeZero[strlen(NegativeZero) - 4] = 'E';
+ S << NegativeZero;
+ return;
+ }
+#else
+ int fpcl = _fpclass(N);
+
+ // negative zero
+ if (fpcl == _FPCLASS_NZ) {
+ char NegativeZero[] = "-0.000000e+00";
+ if (Style == FloatStyle::ExponentUpper)
+ NegativeZero[strlen(NegativeZero) - 4] = 'E';
+ S << NegativeZero;
+ return;
+ }
+#endif
+
+ char buf[32];
+ unsigned len;
+ len = format(Spec.c_str(), N).snprint(buf, sizeof(buf));
+ if (len <= sizeof(buf) - 2) {
+ if (len >= 5 && (buf[len - 5] == 'e' || buf[len - 5] == 'E') &&
+ buf[len - 3] == '0') {
+ int cs = buf[len - 4];
+ if (cs == '+' || cs == '-') {
+ int c1 = buf[len - 2];
+ int c0 = buf[len - 1];
+ if (isdigit(static_cast<unsigned char>(c1)) &&
+ isdigit(static_cast<unsigned char>(c0))) {
+ // Trim leading '0': "...e+012" -> "...e+12\0"
+ buf[len - 3] = c1;
+ buf[len - 2] = c0;
+ buf[--len] = 0;
+ }
+ }
+ }
+ S << buf;
+ return;
+ }
+#endif
+ }
+
+ if (Style == FloatStyle::Percent)
+ N *= 100.0;
+
+ char Buf[32];
+ format(Spec.c_str(), N).snprint(Buf, sizeof(Buf));
+ S << Buf;
+ if (Style == FloatStyle::Percent)
+ S << '%';
+}
+
+bool wpi::isPrefixedHexStyle(HexPrintStyle S) {
+ return (S == HexPrintStyle::PrefixLower || S == HexPrintStyle::PrefixUpper);
+}
+
+size_t wpi::getDefaultPrecision(FloatStyle Style) {
+ switch (Style) {
+ case FloatStyle::Exponent:
+ case FloatStyle::ExponentUpper:
+ return 6; // Number of decimal places.
+ case FloatStyle::Fixed:
+ case FloatStyle::Percent:
+ return 2; // Number of decimal places.
+ }
+ LLVM_BUILTIN_UNREACHABLE;
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/Path.cpp b/wpiutil/src/main/native/cpp/llvm/Path.cpp
new file mode 100644
index 0000000..49f92d3
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/Path.cpp
@@ -0,0 +1,833 @@
+//===-- Path.cpp - Implement OS Path Concept ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the operating system Path API.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/Path.h"
+#include "wpi/ArrayRef.h"
+#include "wpi/Endian.h"
+#include "wpi/Errc.h"
+#include "wpi/ErrorHandling.h"
+#include "wpi/FileSystem.h"
+#include "wpi/SmallString.h"
+#include <cctype>
+#include <cstring>
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+using namespace wpi;
+using namespace wpi::support::endian;
+
+namespace {
+ using wpi::StringRef;
+ using wpi::sys::path::is_separator;
+ using wpi::sys::path::Style;
+
+ inline Style real_style(Style style) {
+#ifdef _WIN32
+ return (style == Style::posix) ? Style::posix : Style::windows;
+#else
+ return (style == Style::windows) ? Style::windows : Style::posix;
+#endif
+ }
+
+ inline const char *separators(Style style) {
+ if (real_style(style) == Style::windows)
+ return "\\/";
+ return "/";
+ }
+
+ inline char preferred_separator(Style style) {
+ if (real_style(style) == Style::windows)
+ return '\\';
+ return '/';
+ }
+
+ StringRef find_first_component(StringRef path, Style style) {
+ // Look for this first component in the following order.
+ // * empty (in this case we return an empty string)
+ // * either C: or {//,\\}net.
+ // * {/,\}
+ // * {file,directory}name
+
+ if (path.empty())
+ return path;
+
+ if (real_style(style) == Style::windows) {
+ // C:
+ if (path.size() >= 2 &&
+ std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':')
+ return path.substr(0, 2);
+ }
+
+ // //net
+ if ((path.size() > 2) && is_separator(path[0], style) &&
+ path[0] == path[1] && !is_separator(path[2], style)) {
+ // Find the next directory separator.
+ size_t end = path.find_first_of(separators(style), 2);
+ return path.substr(0, end);
+ }
+
+ // {/,\}
+ if (is_separator(path[0], style))
+ return path.substr(0, 1);
+
+ // * {file,directory}name
+ size_t end = path.find_first_of(separators(style));
+ return path.substr(0, end);
+ }
+
+ // Returns the first character of the filename in str. For paths ending in
+ // '/', it returns the position of the '/'.
+ size_t filename_pos(StringRef str, Style style) {
+ if (str.size() > 0 && is_separator(str[str.size() - 1], style))
+ return str.size() - 1;
+
+ size_t pos = str.find_last_of(separators(style), str.size() - 1);
+
+ if (real_style(style) == Style::windows) {
+ if (pos == StringRef::npos)
+ pos = str.find_last_of(':', str.size() - 2);
+ }
+
+ if (pos == StringRef::npos || (pos == 1 && is_separator(str[0], style)))
+ return 0;
+
+ return pos + 1;
+ }
+
+ // Returns the position of the root directory in str. If there is no root
+ // directory in str, it returns StringRef::npos.
+ size_t root_dir_start(StringRef str, Style style) {
+ // case "c:/"
+ if (real_style(style) == Style::windows) {
+ if (str.size() > 2 && str[1] == ':' && is_separator(str[2], style))
+ return 2;
+ }
+
+ // case "//net"
+ if (str.size() > 3 && is_separator(str[0], style) && str[0] == str[1] &&
+ !is_separator(str[2], style)) {
+ return str.find_first_of(separators(style), 2);
+ }
+
+ // case "/"
+ if (str.size() > 0 && is_separator(str[0], style))
+ return 0;
+
+ return StringRef::npos;
+ }
+
+ // Returns the position past the end of the "parent path" of path. The parent
+ // path will not end in '/', unless the parent is the root directory. If the
+ // path has no parent, 0 is returned.
+ size_t parent_path_end(StringRef path, Style style) {
+ size_t end_pos = filename_pos(path, style);
+
+ bool filename_was_sep =
+ path.size() > 0 && is_separator(path[end_pos], style);
+
+ // Skip separators until we reach root dir (or the start of the string).
+ size_t root_dir_pos = root_dir_start(path, style);
+ while (end_pos > 0 &&
+ (root_dir_pos == StringRef::npos || end_pos > root_dir_pos) &&
+ is_separator(path[end_pos - 1], style))
+ --end_pos;
+
+ if (end_pos == root_dir_pos && !filename_was_sep) {
+ // We've reached the root dir and the input path was *not* ending in a
+ // sequence of slashes. Include the root dir in the parent path.
+ return root_dir_pos + 1;
+ }
+
+ // Otherwise, just include before the last slash.
+ return end_pos;
+ }
+} // end unnamed namespace
+
+namespace wpi {
+namespace sys {
+namespace path {
+
+const_iterator begin(StringRef path, Style style) {
+ const_iterator i;
+ i.Path = path;
+ i.Component = find_first_component(path, style);
+ i.Position = 0;
+ i.S = style;
+ return i;
+}
+
+const_iterator end(StringRef path) {
+ const_iterator i;
+ i.Path = path;
+ i.Position = path.size();
+ return i;
+}
+
+const_iterator &const_iterator::operator++() {
+ assert(Position < Path.size() && "Tried to increment past end!");
+
+ // Increment Position to past the current component
+ Position += Component.size();
+
+ // Check for end.
+ if (Position == Path.size()) {
+ Component = StringRef();
+ return *this;
+ }
+
+ // Both POSIX and Windows treat paths that begin with exactly two separators
+ // specially.
+ bool was_net = Component.size() > 2 && is_separator(Component[0], S) &&
+ Component[1] == Component[0] && !is_separator(Component[2], S);
+
+ // Handle separators.
+ if (is_separator(Path[Position], S)) {
+ // Root dir.
+ if (was_net ||
+ // c:/
+ (real_style(S) == Style::windows && Component.endswith(":"))) {
+ Component = Path.substr(Position, 1);
+ return *this;
+ }
+
+ // Skip extra separators.
+ while (Position != Path.size() && is_separator(Path[Position], S)) {
+ ++Position;
+ }
+
+ // Treat trailing '/' as a '.', unless it is the root dir.
+ if (Position == Path.size() && Component != "/") {
+ --Position;
+ Component = ".";
+ return *this;
+ }
+ }
+
+ // Find next component.
+ size_t end_pos = Path.find_first_of(separators(S), Position);
+ Component = Path.slice(Position, end_pos);
+
+ return *this;
+}
+
+bool const_iterator::operator==(const const_iterator &RHS) const {
+ return Path.begin() == RHS.Path.begin() && Position == RHS.Position;
+}
+
+ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const {
+ return Position - RHS.Position;
+}
+
+reverse_iterator rbegin(StringRef Path, Style style) {
+ reverse_iterator I;
+ I.Path = Path;
+ I.Position = Path.size();
+ I.S = style;
+ return ++I;
+}
+
+reverse_iterator rend(StringRef Path) {
+ reverse_iterator I;
+ I.Path = Path;
+ I.Component = Path.substr(0, 0);
+ I.Position = 0;
+ return I;
+}
+
+reverse_iterator &reverse_iterator::operator++() {
+ size_t root_dir_pos = root_dir_start(Path, S);
+
+ // Skip separators unless it's the root directory.
+ size_t end_pos = Position;
+ while (end_pos > 0 && (end_pos - 1) != root_dir_pos &&
+ is_separator(Path[end_pos - 1], S))
+ --end_pos;
+
+ // Treat trailing '/' as a '.', unless it is the root dir.
+ if (Position == Path.size() && !Path.empty() &&
+ is_separator(Path.back(), S) &&
+ (root_dir_pos == StringRef::npos || end_pos - 1 > root_dir_pos)) {
+ --Position;
+ Component = ".";
+ return *this;
+ }
+
+ // Find next separator.
+ size_t start_pos = filename_pos(Path.substr(0, end_pos), S);
+ Component = Path.slice(start_pos, end_pos);
+ Position = start_pos;
+ return *this;
+}
+
+bool reverse_iterator::operator==(const reverse_iterator &RHS) const {
+ return Path.begin() == RHS.Path.begin() && Component == RHS.Component &&
+ Position == RHS.Position;
+}
+
+ptrdiff_t reverse_iterator::operator-(const reverse_iterator &RHS) const {
+ return Position - RHS.Position;
+}
+
+StringRef root_path(StringRef path, Style style) {
+ const_iterator b = begin(path, style), pos = b, e = end(path);
+ if (b != e) {
+ bool has_net =
+ b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0];
+ bool has_drive = (real_style(style) == Style::windows) && b->endswith(":");
+
+ if (has_net || has_drive) {
+ if ((++pos != e) && is_separator((*pos)[0], style)) {
+ // {C:/,//net/}, so get the first two components.
+ return path.substr(0, b->size() + pos->size());
+ } else {
+ // just {C:,//net}, return the first component.
+ return *b;
+ }
+ }
+
+ // POSIX style root directory.
+ if (is_separator((*b)[0], style)) {
+ return *b;
+ }
+ }
+
+ return StringRef();
+}
+
+StringRef root_name(StringRef path, Style style) {
+ const_iterator b = begin(path, style), e = end(path);
+ if (b != e) {
+ bool has_net =
+ b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0];
+ bool has_drive = (real_style(style) == Style::windows) && b->endswith(":");
+
+ if (has_net || has_drive) {
+ // just {C:,//net}, return the first component.
+ return *b;
+ }
+ }
+
+ // No path or no name.
+ return StringRef();
+}
+
+StringRef root_directory(StringRef path, Style style) {
+ const_iterator b = begin(path, style), pos = b, e = end(path);
+ if (b != e) {
+ bool has_net =
+ b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0];
+ bool has_drive = (real_style(style) == Style::windows) && b->endswith(":");
+
+ if ((has_net || has_drive) &&
+ // {C:,//net}, skip to the next component.
+ (++pos != e) && is_separator((*pos)[0], style)) {
+ return *pos;
+ }
+
+ // POSIX style root directory.
+ if (!has_net && is_separator((*b)[0], style)) {
+ return *b;
+ }
+ }
+
+ // No path or no root.
+ return StringRef();
+}
+
+StringRef relative_path(StringRef path, Style style) {
+ StringRef root = root_path(path, style);
+ return path.substr(root.size());
+}
+
+void append(SmallVectorImpl<char> &path, Style style, const Twine &a,
+ const Twine &b, const Twine &c, const Twine &d) {
+ SmallString<32> a_storage;
+ SmallString<32> b_storage;
+ SmallString<32> c_storage;
+ SmallString<32> d_storage;
+
+ SmallVector<StringRef, 4> components;
+ if (!a.isTriviallyEmpty()) components.push_back(a.toStringRef(a_storage));
+ if (!b.isTriviallyEmpty()) components.push_back(b.toStringRef(b_storage));
+ if (!c.isTriviallyEmpty()) components.push_back(c.toStringRef(c_storage));
+ if (!d.isTriviallyEmpty()) components.push_back(d.toStringRef(d_storage));
+
+ for (auto &component : components) {
+ bool path_has_sep =
+ !path.empty() && is_separator(path[path.size() - 1], style);
+ if (path_has_sep) {
+ // Strip separators from beginning of component.
+ size_t loc = component.find_first_not_of(separators(style));
+ StringRef c = component.substr(loc);
+
+ // Append it.
+ path.append(c.begin(), c.end());
+ continue;
+ }
+
+ bool component_has_sep =
+ !component.empty() && is_separator(component[0], style);
+ if (!component_has_sep &&
+ !(path.empty() || has_root_name(component, style))) {
+ // Add a separator.
+ path.push_back(preferred_separator(style));
+ }
+
+ path.append(component.begin(), component.end());
+ }
+}
+
+void append(SmallVectorImpl<char> &path, const Twine &a, const Twine &b,
+ const Twine &c, const Twine &d) {
+ append(path, Style::native, a, b, c, d);
+}
+
+void append(SmallVectorImpl<char> &path, const_iterator begin,
+ const_iterator end, Style style) {
+ for (; begin != end; ++begin)
+ path::append(path, style, *begin);
+}
+
+StringRef parent_path(StringRef path, Style style) {
+ size_t end_pos = parent_path_end(path, style);
+ if (end_pos == StringRef::npos)
+ return StringRef();
+ else
+ return path.substr(0, end_pos);
+}
+
+void remove_filename(SmallVectorImpl<char> &path, Style style) {
+ size_t end_pos = parent_path_end(StringRef(path.begin(), path.size()), style);
+ if (end_pos != StringRef::npos)
+ path.set_size(end_pos);
+}
+
+void replace_extension(SmallVectorImpl<char> &path, const Twine &extension,
+ Style style) {
+ StringRef p(path.begin(), path.size());
+ SmallString<32> ext_storage;
+ StringRef ext = extension.toStringRef(ext_storage);
+
+ // Erase existing extension.
+ size_t pos = p.find_last_of('.');
+ if (pos != StringRef::npos && pos >= filename_pos(p, style))
+ path.set_size(pos);
+
+ // Append '.' if needed.
+ if (ext.size() > 0 && ext[0] != '.')
+ path.push_back('.');
+
+ // Append extension.
+ path.append(ext.begin(), ext.end());
+}
+
+void replace_path_prefix(SmallVectorImpl<char> &Path,
+ const StringRef &OldPrefix, const StringRef &NewPrefix,
+ Style style) {
+ if (OldPrefix.empty() && NewPrefix.empty())
+ return;
+
+ StringRef OrigPath(Path.begin(), Path.size());
+ if (!OrigPath.startswith(OldPrefix))
+ return;
+
+ // If prefixes have the same size we can simply copy the new one over.
+ if (OldPrefix.size() == NewPrefix.size()) {
+ wpi::copy(NewPrefix, Path.begin());
+ return;
+ }
+
+ StringRef RelPath = OrigPath.substr(OldPrefix.size());
+ SmallString<256> NewPath;
+ path::append(NewPath, style, NewPrefix);
+ path::append(NewPath, style, RelPath);
+ Path.swap(NewPath);
+}
+
+void native(const Twine &path, SmallVectorImpl<char> &result, Style style) {
+ assert((!path.isSingleStringRef() ||
+ path.getSingleStringRef().data() != result.data()) &&
+ "path and result are not allowed to overlap!");
+ // Clear result.
+ result.clear();
+ path.toVector(result);
+ native(result, style);
+}
+
+void native(SmallVectorImpl<char> &Path, Style style) {
+ if (Path.empty())
+ return;
+ if (real_style(style) == Style::windows) {
+ std::replace(Path.begin(), Path.end(), '/', '\\');
+ if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) {
+ SmallString<128> PathHome;
+ home_directory(PathHome);
+ PathHome.append(Path.begin() + 1, Path.end());
+ Path = PathHome;
+ }
+ } else {
+ for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) {
+ if (*PI == '\\') {
+ auto PN = PI + 1;
+ if (PN < PE && *PN == '\\')
+ ++PI; // increment once, the for loop will move over the escaped slash
+ else
+ *PI = '/';
+ }
+ }
+ }
+}
+
+std::string convert_to_slash(StringRef path, Style style) {
+ if (real_style(style) != Style::windows)
+ return path;
+
+ std::string s = path.str();
+ std::replace(s.begin(), s.end(), '\\', '/');
+ return s;
+}
+
+StringRef filename(StringRef path, Style style) { return *rbegin(path, style); }
+
+StringRef stem(StringRef path, Style style) {
+ StringRef fname = filename(path, style);
+ size_t pos = fname.find_last_of('.');
+ if (pos == StringRef::npos)
+ return fname;
+ else
+ if ((fname.size() == 1 && fname == ".") ||
+ (fname.size() == 2 && fname == ".."))
+ return fname;
+ else
+ return fname.substr(0, pos);
+}
+
+StringRef extension(StringRef path, Style style) {
+ StringRef fname = filename(path, style);
+ size_t pos = fname.find_last_of('.');
+ if (pos == StringRef::npos)
+ return StringRef();
+ else
+ if ((fname.size() == 1 && fname == ".") ||
+ (fname.size() == 2 && fname == ".."))
+ return StringRef();
+ else
+ return fname.substr(pos);
+}
+
+bool is_separator(char value, Style style) {
+ if (value == '/')
+ return true;
+ if (real_style(style) == Style::windows)
+ return value == '\\';
+ return false;
+}
+
+StringRef get_separator(Style style) {
+ if (real_style(style) == Style::windows)
+ return "\\";
+ return "/";
+}
+
+bool has_root_name(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !root_name(p, style).empty();
+}
+
+bool has_root_directory(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !root_directory(p, style).empty();
+}
+
+bool has_root_path(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !root_path(p, style).empty();
+}
+
+bool has_relative_path(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !relative_path(p, style).empty();
+}
+
+bool has_filename(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !filename(p, style).empty();
+}
+
+bool has_parent_path(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !parent_path(p, style).empty();
+}
+
+bool has_stem(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !stem(p, style).empty();
+}
+
+bool has_extension(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ return !extension(p, style).empty();
+}
+
+bool is_absolute(const Twine &path, Style style) {
+ SmallString<128> path_storage;
+ StringRef p = path.toStringRef(path_storage);
+
+ bool rootDir = has_root_directory(p, style);
+ bool rootName =
+ (real_style(style) != Style::windows) || has_root_name(p, style);
+
+ return rootDir && rootName;
+}
+
+bool is_relative(const Twine &path, Style style) {
+ return !is_absolute(path, style);
+}
+
+StringRef remove_leading_dotslash(StringRef Path, Style style) {
+ // Remove leading "./" (or ".//" or "././" etc.)
+ while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1], style)) {
+ Path = Path.substr(2);
+ while (Path.size() > 0 && is_separator(Path[0], style))
+ Path = Path.substr(1);
+ }
+ return Path;
+}
+
+static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot,
+ Style style) {
+ SmallVector<StringRef, 16> components;
+
+ // Skip the root path, then look for traversal in the components.
+ StringRef rel = path::relative_path(path, style);
+ for (StringRef C :
+ wpi::make_range(path::begin(rel, style), path::end(rel))) {
+ if (C == ".")
+ continue;
+ // Leading ".." will remain in the path unless it's at the root.
+ if (remove_dot_dot && C == "..") {
+ if (!components.empty() && components.back() != "..") {
+ components.pop_back();
+ continue;
+ }
+ if (path::is_absolute(path, style))
+ continue;
+ }
+ components.push_back(C);
+ }
+
+ SmallString<256> buffer = path::root_path(path, style);
+ for (StringRef C : components)
+ path::append(buffer, style, C);
+ return buffer;
+}
+
+bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot,
+ Style style) {
+ StringRef p(path.data(), path.size());
+
+ SmallString<256> result = remove_dots(p, remove_dot_dot, style);
+ if (result == path)
+ return false;
+
+ path.swap(result);
+ return true;
+}
+
+} // end namespace path
+
+namespace fs {
+
+std::error_code getUniqueID(const Twine Path, UniqueID &Result) {
+ file_status Status;
+ std::error_code EC = status(Path, Status);
+ if (EC)
+ return EC;
+ Result = Status.getUniqueID();
+ return std::error_code();
+}
+
+void make_absolute(const Twine ¤t_directory,
+ SmallVectorImpl<char> &path) {
+ StringRef p(path.data(), path.size());
+
+ bool rootDirectory = path::has_root_directory(p);
+ bool rootName =
+ (real_style(Style::native) != Style::windows) || path::has_root_name(p);
+
+ // Already absolute.
+ if (rootName && rootDirectory)
+ return;
+
+ // All of the following conditions will need the current directory.
+ SmallString<128> current_dir;
+ current_directory.toVector(current_dir);
+
+ // Relative path. Prepend the current directory.
+ if (!rootName && !rootDirectory) {
+ // Append path to the current directory.
+ path::append(current_dir, p);
+ // Set path to the result.
+ path.swap(current_dir);
+ return;
+ }
+
+ if (!rootName && rootDirectory) {
+ StringRef cdrn = path::root_name(current_dir);
+ SmallString<128> curDirRootName(cdrn.begin(), cdrn.end());
+ path::append(curDirRootName, p);
+ // Set path to the result.
+ path.swap(curDirRootName);
+ return;
+ }
+
+ if (rootName && !rootDirectory) {
+ StringRef pRootName = path::root_name(p);
+ StringRef bRootDirectory = path::root_directory(current_dir);
+ StringRef bRelativePath = path::relative_path(current_dir);
+ StringRef pRelativePath = path::relative_path(p);
+
+ SmallString<128> res;
+ path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath);
+ path.swap(res);
+ return;
+ }
+
+ wpi_unreachable("All rootName and rootDirectory combinations should have "
+ "occurred above!");
+}
+
+std::error_code make_absolute(SmallVectorImpl<char> &path) {
+ if (path::is_absolute(path))
+ return {};
+
+ SmallString<128> current_dir;
+ if (std::error_code ec = current_path(current_dir))
+ return ec;
+
+ make_absolute(current_dir, path);
+ return {};
+}
+
+bool exists(const basic_file_status &status) {
+ return status_known(status) && status.type() != file_type::file_not_found;
+}
+
+bool status_known(const basic_file_status &s) {
+ return s.type() != file_type::status_error;
+}
+
+file_type get_file_type(const Twine &Path, bool Follow) {
+ file_status st;
+ if (status(Path, st, Follow))
+ return file_type::status_error;
+ return st.type();
+}
+
+bool is_directory(const basic_file_status &status) {
+ return status.type() == file_type::directory_file;
+}
+
+std::error_code is_directory(const Twine &path, bool &result) {
+ file_status st;
+ if (std::error_code ec = status(path, st))
+ return ec;
+ result = is_directory(st);
+ return std::error_code();
+}
+
+bool is_regular_file(const basic_file_status &status) {
+ return status.type() == file_type::regular_file;
+}
+
+std::error_code is_regular_file(const Twine &path, bool &result) {
+ file_status st;
+ if (std::error_code ec = status(path, st))
+ return ec;
+ result = is_regular_file(st);
+ return std::error_code();
+}
+
+bool is_symlink_file(const basic_file_status &status) {
+ return status.type() == file_type::symlink_file;
+}
+
+std::error_code is_symlink_file(const Twine &path, bool &result) {
+ file_status st;
+ if (std::error_code ec = status(path, st, false))
+ return ec;
+ result = is_symlink_file(st);
+ return std::error_code();
+}
+
+bool is_other(const basic_file_status &status) {
+ return exists(status) &&
+ !is_regular_file(status) &&
+ !is_directory(status);
+}
+
+std::error_code is_other(const Twine &Path, bool &Result) {
+ file_status FileStatus;
+ if (std::error_code EC = status(Path, FileStatus))
+ return EC;
+ Result = is_other(FileStatus);
+ return std::error_code();
+}
+
+void directory_entry::replace_filename(const Twine &Filename, file_type Type,
+ basic_file_status Status) {
+ SmallString<128> PathStr = path::parent_path(Path);
+ path::append(PathStr, Filename);
+ this->Path = PathStr.str();
+ this->Type = Type;
+ this->Status = Status;
+}
+
+ErrorOr<perms> getPermissions(const Twine &Path) {
+ file_status Status;
+ if (std::error_code EC = status(Path, Status))
+ return EC;
+
+ return Status.permissions();
+}
+
+} // end namespace fs
+} // end namespace sys
+} // end namespace wpi
+
+// Include the truly platform-specific parts.
+#ifdef _WIN32
+#include "Windows/Path.inc"
+#else
+#include "Unix/Path.inc"
+#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
new file mode 100644
index 0000000..1dab1fc
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
@@ -0,0 +1,270 @@
+//===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the SmallPtrSet class. See SmallPtrSet.h for an
+// overview of the algorithm.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/SmallPtrSet.h"
+#include "wpi/DenseMapInfo.h"
+#include "wpi/MathExtras.h"
+#include "wpi/ErrorHandling.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+
+using namespace wpi;
+
+void SmallPtrSetImplBase::shrink_and_clear() {
+ assert(!isSmall() && "Can't shrink a small set!");
+ free(CurArray);
+
+ // Reduce the number of buckets.
+ unsigned Size = size();
+ CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
+ NumNonEmpty = NumTombstones = 0;
+
+ // Install the new array. Clear all the buckets to empty.
+ CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
+
+ memset(CurArray, -1, CurArraySize*sizeof(void*));
+}
+
+std::pair<const void *const *, bool>
+SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
+ if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
+ // If more than 3/4 of the array is full, grow.
+ Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
+ } else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
+ // If fewer of 1/8 of the array is empty (meaning that many are filled with
+ // tombstones), rehash.
+ Grow(CurArraySize);
+ }
+
+ // Okay, we know we have space. Find a hash bucket.
+ const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
+ if (*Bucket == Ptr)
+ return std::make_pair(Bucket, false); // Already inserted, good.
+
+ // Otherwise, insert it!
+ if (*Bucket == getTombstoneMarker())
+ --NumTombstones;
+ else
+ ++NumNonEmpty; // Track density.
+ *Bucket = Ptr;
+ return std::make_pair(Bucket, true);
+}
+
+const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
+ unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
+ unsigned ArraySize = CurArraySize;
+ unsigned ProbeAmt = 1;
+ const void *const *Array = CurArray;
+ const void *const *Tombstone = nullptr;
+ while (true) {
+ // If we found an empty bucket, the pointer doesn't exist in the set.
+ // Return a tombstone if we've seen one so far, or the empty bucket if
+ // not.
+ if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
+ return Tombstone ? Tombstone : Array+Bucket;
+
+ // Found Ptr's bucket?
+ if (LLVM_LIKELY(Array[Bucket] == Ptr))
+ return Array+Bucket;
+
+ // If this is a tombstone, remember it. If Ptr ends up not in the set, we
+ // prefer to return it than something that would require more probing.
+ if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
+ Tombstone = Array+Bucket; // Remember the first tombstone found.
+
+ // It's a hash collision or a tombstone. Reprobe.
+ Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
+ }
+}
+
+/// Grow - Allocate a larger backing store for the buckets and move it over.
+///
+void SmallPtrSetImplBase::Grow(unsigned NewSize) {
+ const void **OldBuckets = CurArray;
+ const void **OldEnd = EndPointer();
+ bool WasSmall = isSmall();
+
+ // Install the new array. Clear all the buckets to empty.
+ const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
+
+ // Reset member only if memory was allocated successfully
+ CurArray = NewBuckets;
+ CurArraySize = NewSize;
+ memset(CurArray, -1, NewSize*sizeof(void*));
+
+ // Copy over all valid entries.
+ for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
+ // Copy over the element if it is valid.
+ const void *Elt = *BucketPtr;
+ if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
+ *const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
+ }
+
+ if (!WasSmall)
+ free(OldBuckets);
+ NumNonEmpty -= NumTombstones;
+ NumTombstones = 0;
+}
+
+SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
+ const SmallPtrSetImplBase &that) {
+ SmallArray = SmallStorage;
+
+ // If we're becoming small, prepare to insert into our stack space
+ if (that.isSmall()) {
+ CurArray = SmallArray;
+ // Otherwise, allocate new heap space (unless we were the same size)
+ } else {
+ CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
+ }
+
+ // Copy over the that array.
+ CopyHelper(that);
+}
+
+SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
+ unsigned SmallSize,
+ SmallPtrSetImplBase &&that) {
+ SmallArray = SmallStorage;
+ MoveHelper(SmallSize, std::move(that));
+}
+
+void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
+ assert(&RHS != this && "Self-copy should be handled by the caller.");
+
+ if (isSmall() && RHS.isSmall())
+ assert(CurArraySize == RHS.CurArraySize &&
+ "Cannot assign sets with different small sizes");
+
+ // If we're becoming small, prepare to insert into our stack space
+ if (RHS.isSmall()) {
+ if (!isSmall())
+ free(CurArray);
+ CurArray = SmallArray;
+ // Otherwise, allocate new heap space (unless we were the same size)
+ } else if (CurArraySize != RHS.CurArraySize) {
+ if (isSmall())
+ CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
+ else {
+ const void **T = (const void**)safe_realloc(CurArray,
+ sizeof(void*) * RHS.CurArraySize);
+ CurArray = T;
+ }
+ }
+
+ CopyHelper(RHS);
+}
+
+void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
+ // Copy over the new array size
+ CurArraySize = RHS.CurArraySize;
+
+ // Copy over the contents from the other set
+ std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
+
+ NumNonEmpty = RHS.NumNonEmpty;
+ NumTombstones = RHS.NumTombstones;
+}
+
+void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
+ SmallPtrSetImplBase &&RHS) {
+ if (!isSmall())
+ free(CurArray);
+ MoveHelper(SmallSize, std::move(RHS));
+}
+
+void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
+ SmallPtrSetImplBase &&RHS) {
+ assert(&RHS != this && "Self-move should be handled by the caller.");
+
+ if (RHS.isSmall()) {
+ // Copy a small RHS rather than moving.
+ CurArray = SmallArray;
+ std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
+ } else {
+ CurArray = RHS.CurArray;
+ RHS.CurArray = RHS.SmallArray;
+ }
+
+ // Copy the rest of the trivial members.
+ CurArraySize = RHS.CurArraySize;
+ NumNonEmpty = RHS.NumNonEmpty;
+ NumTombstones = RHS.NumTombstones;
+
+ // Make the RHS small and empty.
+ RHS.CurArraySize = SmallSize;
+ assert(RHS.CurArray == RHS.SmallArray);
+ RHS.NumNonEmpty = 0;
+ RHS.NumTombstones = 0;
+}
+
+void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
+ if (this == &RHS) return;
+
+ // We can only avoid copying elements if neither set is small.
+ if (!this->isSmall() && !RHS.isSmall()) {
+ std::swap(this->CurArray, RHS.CurArray);
+ std::swap(this->CurArraySize, RHS.CurArraySize);
+ std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
+ std::swap(this->NumTombstones, RHS.NumTombstones);
+ return;
+ }
+
+ // FIXME: From here on we assume that both sets have the same small size.
+
+ // If only RHS is small, copy the small elements into LHS and move the pointer
+ // from LHS to RHS.
+ if (!this->isSmall() && RHS.isSmall()) {
+ assert(RHS.CurArray == RHS.SmallArray);
+ std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
+ std::swap(RHS.CurArraySize, this->CurArraySize);
+ std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
+ std::swap(this->NumTombstones, RHS.NumTombstones);
+ RHS.CurArray = this->CurArray;
+ this->CurArray = this->SmallArray;
+ return;
+ }
+
+ // If only LHS is small, copy the small elements into RHS and move the pointer
+ // from RHS to LHS.
+ if (this->isSmall() && !RHS.isSmall()) {
+ assert(this->CurArray == this->SmallArray);
+ std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
+ RHS.SmallArray);
+ std::swap(RHS.CurArraySize, this->CurArraySize);
+ std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
+ std::swap(RHS.NumTombstones, this->NumTombstones);
+ this->CurArray = RHS.CurArray;
+ RHS.CurArray = RHS.SmallArray;
+ return;
+ }
+
+ // Both a small, just swap the small elements.
+ assert(this->isSmall() && RHS.isSmall());
+ unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
+ std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
+ RHS.SmallArray);
+ if (this->NumNonEmpty > MinNonEmpty) {
+ std::copy(this->SmallArray + MinNonEmpty,
+ this->SmallArray + this->NumNonEmpty,
+ RHS.SmallArray + MinNonEmpty);
+ } else {
+ std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
+ this->SmallArray + MinNonEmpty);
+ }
+ assert(this->CurArraySize == RHS.CurArraySize);
+ std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
+ std::swap(this->NumTombstones, RHS.NumTombstones);
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp
new file mode 100644
index 0000000..974fec9
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp
@@ -0,0 +1,43 @@
+//===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the SmallVector class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/SmallVector.h"
+#include "wpi/MemAlloc.h"
+using namespace wpi;
+
+/// grow_pod - This is an implementation of the grow() method which only works
+/// on POD-like datatypes and is out of line to reduce code duplication.
+void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity,
+ size_t TSize) {
+ // Ensure we can fit the new capacity in 32 bits.
+ if (MinCapacity > UINT32_MAX)
+ report_bad_alloc_error("SmallVector capacity overflow during allocation");
+
+ size_t NewCapacity = 2 * capacity() + 1; // Always grow.
+ NewCapacity =
+ std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX));
+
+ void *NewElts;
+ if (BeginX == FirstEl) {
+ NewElts = safe_malloc(NewCapacity * TSize);
+
+ // Copy the elements over. No need to run dtors on PODs.
+ memcpy(NewElts, this->BeginX, size() * TSize);
+ } else {
+ // If this wasn't grown from the inline copy, grow the allocated space.
+ NewElts = safe_realloc(this->BeginX, NewCapacity * TSize);
+ }
+
+ this->BeginX = NewElts;
+ this->Capacity = NewCapacity;
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp b/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp
new file mode 100644
index 0000000..e4bfe8a
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp
@@ -0,0 +1,91 @@
+//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the StringExtras.h header
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/StringExtras.h"
+#include "wpi/SmallVector.h"
+#include "wpi/raw_ostream.h"
+using namespace wpi;
+
+/// StrInStrNoCase - Portable version of strcasestr. Locates the first
+/// occurrence of string 's1' in string 's2', ignoring case. Returns
+/// the offset of s2 in s1 or npos if s2 cannot be found.
+StringRef::size_type wpi::StrInStrNoCase(StringRef s1, StringRef s2) {
+ size_t N = s2.size(), M = s1.size();
+ if (N > M)
+ return StringRef::npos;
+ for (size_t i = 0, e = M - N + 1; i != e; ++i)
+ if (s1.substr(i, N).equals_lower(s2))
+ return i;
+ return StringRef::npos;
+}
+
+/// getToken - This function extracts one token from source, ignoring any
+/// leading characters that appear in the Delimiters string, and ending the
+/// token at any of the characters that appear in the Delimiters string. If
+/// there are no tokens in the source string, an empty string is returned.
+/// The function returns a pair containing the extracted token and the
+/// remaining tail string.
+std::pair<StringRef, StringRef> wpi::getToken(StringRef Source,
+ StringRef Delimiters) {
+ // Figure out where the token starts.
+ StringRef::size_type Start = Source.find_first_not_of(Delimiters);
+
+ // Find the next occurrence of the delimiter.
+ StringRef::size_type End = Source.find_first_of(Delimiters, Start);
+
+ return std::make_pair(Source.slice(Start, End), Source.substr(End));
+}
+
+/// SplitString - Split up the specified string according to the specified
+/// delimiters, appending the result fragments to the output list.
+void wpi::SplitString(StringRef Source,
+ SmallVectorImpl<StringRef> &OutFragments,
+ StringRef Delimiters) {
+ std::pair<StringRef, StringRef> S = getToken(Source, Delimiters);
+ while (!S.first.empty()) {
+ OutFragments.push_back(S.first);
+ S = getToken(S.second, Delimiters);
+ }
+}
+
+void wpi::printEscapedString(StringRef Name, raw_ostream &Out) {
+ for (unsigned i = 0, e = Name.size(); i != e; ++i) {
+ unsigned char C = Name[i];
+ if (isPrint(C) && C != '\\' && C != '"')
+ Out << C;
+ else
+ Out << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
+ }
+}
+
+void wpi::printHTMLEscaped(StringRef String, raw_ostream &Out) {
+ for (char C : String) {
+ if (C == '&')
+ Out << "&";
+ else if (C == '<')
+ Out << "<";
+ else if (C == '>')
+ Out << ">";
+ else if (C == '\"')
+ Out << """;
+ else if (C == '\'')
+ Out << "'";
+ else
+ Out << C;
+ }
+}
+
+void wpi::printLowerCase(StringRef String, raw_ostream &Out) {
+ for (const char C : String)
+ Out << toLower(C);
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp
new file mode 100644
index 0000000..5c625c7
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp
@@ -0,0 +1,261 @@
+//===--- StringMap.cpp - String Hash table map implementation -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the StringMap class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/StringMap.h"
+#include "wpi/StringExtras.h"
+#include "wpi/Compiler.h"
+#include "wpi/MathExtras.h"
+#include <cassert>
+
+using namespace wpi;
+
+/// Returns the number of buckets to allocate to ensure that the DenseMap can
+/// accommodate \p NumEntries without need to grow().
+static unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
+ // Ensure that "NumEntries * 4 < NumBuckets * 3"
+ if (NumEntries == 0)
+ return 0;
+ // +1 is required because of the strict equality.
+ // For example if NumEntries is 48, we need to return 401.
+ return NextPowerOf2(NumEntries * 4 / 3 + 1);
+}
+
+StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) {
+ ItemSize = itemSize;
+
+ // If a size is specified, initialize the table with that many buckets.
+ if (InitSize) {
+ // The table will grow when the number of entries reach 3/4 of the number of
+ // buckets. To guarantee that "InitSize" number of entries can be inserted
+ // in the table without growing, we allocate just what is needed here.
+ init(getMinBucketToReserveForEntries(InitSize));
+ return;
+ }
+
+ // Otherwise, initialize it with zero buckets to avoid the allocation.
+ TheTable = nullptr;
+ NumBuckets = 0;
+ NumItems = 0;
+ NumTombstones = 0;
+}
+
+void StringMapImpl::init(unsigned InitSize) {
+ assert((InitSize & (InitSize-1)) == 0 &&
+ "Init Size must be a power of 2 or zero!");
+
+ unsigned NewNumBuckets = InitSize ? InitSize : 16;
+ NumItems = 0;
+ NumTombstones = 0;
+
+ TheTable = static_cast<StringMapEntryBase **>(
+ safe_calloc(NewNumBuckets+1,
+ sizeof(StringMapEntryBase **) + sizeof(unsigned)));
+
+ // Set the member only if TheTable was successfully allocated
+ NumBuckets = NewNumBuckets;
+
+ // Allocate one extra bucket, set it to look filled so the iterators stop at
+ // end.
+ TheTable[NumBuckets] = (StringMapEntryBase*)2;
+}
+
+/// LookupBucketFor - Look up the bucket that the specified string should end
+/// up in. If it already exists as a key in the map, the Item pointer for the
+/// specified bucket will be non-null. Otherwise, it will be null. In either
+/// case, the FullHashValue field of the bucket will be set to the hash value
+/// of the string.
+unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
+ unsigned HTSize = NumBuckets;
+ if (HTSize == 0) { // Hash table unallocated so far?
+ init(16);
+ HTSize = NumBuckets;
+ }
+ unsigned FullHashValue = HashString(Name);
+ unsigned BucketNo = FullHashValue & (HTSize-1);
+ unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
+
+ unsigned ProbeAmt = 1;
+ int FirstTombstone = -1;
+ while (true) {
+ StringMapEntryBase *BucketItem = TheTable[BucketNo];
+ // If we found an empty bucket, this key isn't in the table yet, return it.
+ if (LLVM_LIKELY(!BucketItem)) {
+ // If we found a tombstone, we want to reuse the tombstone instead of an
+ // empty bucket. This reduces probing.
+ if (FirstTombstone != -1) {
+ HashTable[FirstTombstone] = FullHashValue;
+ return FirstTombstone;
+ }
+
+ HashTable[BucketNo] = FullHashValue;
+ return BucketNo;
+ }
+
+ if (BucketItem == getTombstoneVal()) {
+ // Skip over tombstones. However, remember the first one we see.
+ if (FirstTombstone == -1) FirstTombstone = BucketNo;
+ } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
+ // If the full hash value matches, check deeply for a match. The common
+ // case here is that we are only looking at the buckets (for item info
+ // being non-null and for the full hash value) not at the items. This
+ // is important for cache locality.
+
+ // Do the comparison like this because Name isn't necessarily
+ // null-terminated!
+ char *ItemStr = (char*)BucketItem+ItemSize;
+ if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) {
+ // We found a match!
+ return BucketNo;
+ }
+ }
+
+ // Okay, we didn't find the item. Probe to the next bucket.
+ BucketNo = (BucketNo+ProbeAmt) & (HTSize-1);
+
+ // Use quadratic probing, it has fewer clumping artifacts than linear
+ // probing and has good cache behavior in the common case.
+ ++ProbeAmt;
+ }
+}
+
+/// FindKey - Look up the bucket that contains the specified key. If it exists
+/// in the map, return the bucket number of the key. Otherwise return -1.
+/// This does not modify the map.
+int StringMapImpl::FindKey(StringRef Key) const {
+ unsigned HTSize = NumBuckets;
+ if (HTSize == 0) return -1; // Really empty table?
+ unsigned FullHashValue = HashString(Key);
+ unsigned BucketNo = FullHashValue & (HTSize-1);
+ unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
+
+ unsigned ProbeAmt = 1;
+ while (true) {
+ StringMapEntryBase *BucketItem = TheTable[BucketNo];
+ // If we found an empty bucket, this key isn't in the table yet, return.
+ if (LLVM_LIKELY(!BucketItem))
+ return -1;
+
+ if (BucketItem == getTombstoneVal()) {
+ // Ignore tombstones.
+ } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
+ // If the full hash value matches, check deeply for a match. The common
+ // case here is that we are only looking at the buckets (for item info
+ // being non-null and for the full hash value) not at the items. This
+ // is important for cache locality.
+
+ // Do the comparison like this because NameStart isn't necessarily
+ // null-terminated!
+ char *ItemStr = (char*)BucketItem+ItemSize;
+ if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) {
+ // We found a match!
+ return BucketNo;
+ }
+ }
+
+ // Okay, we didn't find the item. Probe to the next bucket.
+ BucketNo = (BucketNo+ProbeAmt) & (HTSize-1);
+
+ // Use quadratic probing, it has fewer clumping artifacts than linear
+ // probing and has good cache behavior in the common case.
+ ++ProbeAmt;
+ }
+}
+
+/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
+/// delete it. This aborts if the value isn't in the table.
+void StringMapImpl::RemoveKey(StringMapEntryBase *V) {
+ const char *VStr = (char*)V + ItemSize;
+ StringMapEntryBase *V2 = RemoveKey(StringRef(VStr, V->getKeyLength()));
+ (void)V2;
+ assert(V == V2 && "Didn't find key?");
+}
+
+/// RemoveKey - Remove the StringMapEntry for the specified key from the
+/// table, returning it. If the key is not in the table, this returns null.
+StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) {
+ int Bucket = FindKey(Key);
+ if (Bucket == -1) return nullptr;
+
+ StringMapEntryBase *Result = TheTable[Bucket];
+ TheTable[Bucket] = getTombstoneVal();
+ --NumItems;
+ ++NumTombstones;
+ assert(NumItems + NumTombstones <= NumBuckets);
+
+ return Result;
+}
+
+/// RehashTable - Grow the table, redistributing values into the buckets with
+/// the appropriate mod-of-hashtable-size.
+unsigned StringMapImpl::RehashTable(unsigned BucketNo) {
+ unsigned NewSize;
+ unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
+
+ // If the hash table is now more than 3/4 full, or if fewer than 1/8 of
+ // the buckets are empty (meaning that many are filled with tombstones),
+ // grow/rehash the table.
+ if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) {
+ NewSize = NumBuckets*2;
+ } else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <=
+ NumBuckets / 8)) {
+ NewSize = NumBuckets;
+ } else {
+ return BucketNo;
+ }
+
+ unsigned NewBucketNo = BucketNo;
+ // Allocate one extra bucket which will always be non-empty. This allows the
+ // iterators to stop at end.
+ auto NewTableArray = static_cast<StringMapEntryBase **>(
+ safe_calloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned)));
+
+ unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1);
+ NewTableArray[NewSize] = (StringMapEntryBase*)2;
+
+ // Rehash all the items into their new buckets. Luckily :) we already have
+ // the hash values available, so we don't have to rehash any strings.
+ for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
+ StringMapEntryBase *Bucket = TheTable[I];
+ if (Bucket && Bucket != getTombstoneVal()) {
+ // Fast case, bucket available.
+ unsigned FullHash = HashTable[I];
+ unsigned NewBucket = FullHash & (NewSize-1);
+ if (!NewTableArray[NewBucket]) {
+ NewTableArray[FullHash & (NewSize-1)] = Bucket;
+ NewHashArray[FullHash & (NewSize-1)] = FullHash;
+ if (I == BucketNo)
+ NewBucketNo = NewBucket;
+ continue;
+ }
+
+ // Otherwise probe for a spot.
+ unsigned ProbeSize = 1;
+ do {
+ NewBucket = (NewBucket + ProbeSize++) & (NewSize-1);
+ } while (NewTableArray[NewBucket]);
+
+ // Finally found a slot. Fill it in.
+ NewTableArray[NewBucket] = Bucket;
+ NewHashArray[NewBucket] = FullHash;
+ if (I == BucketNo)
+ NewBucketNo = NewBucket;
+ }
+ }
+
+ free(TheTable);
+
+ TheTable = NewTableArray;
+ NumBuckets = NewSize;
+ NumTombstones = 0;
+ return NewBucketNo;
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/StringRef.cpp b/wpiutil/src/main/native/cpp/llvm/StringRef.cpp
new file mode 100644
index 0000000..ea44bea
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/StringRef.cpp
@@ -0,0 +1,507 @@
+//===-- StringRef.cpp - Lightweight String References ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/StringRef.h"
+#include "wpi/Hashing.h"
+#include "wpi/StringExtras.h"
+#include "wpi/SmallVector.h"
+#include <bitset>
+#include <climits>
+#include <ostream>
+
+using namespace wpi;
+
+// MSVC emits references to this into the translation units which reference it.
+#ifndef _MSC_VER
+const size_t StringRef::npos;
+#endif
+
+// strncasecmp() is not available on non-POSIX systems, so define an
+// alternative function here.
+static int ascii_strncasecmp(const char *LHS, const char *RHS, size_t Length) noexcept {
+ for (size_t I = 0; I < Length; ++I) {
+ unsigned char LHC = toLower(LHS[I]);
+ unsigned char RHC = toLower(RHS[I]);
+ if (LHC != RHC)
+ return LHC < RHC ? -1 : 1;
+ }
+ return 0;
+}
+
+/// compare_lower - Compare strings, ignoring case.
+int StringRef::compare_lower(StringRef RHS) const noexcept {
+ if (int Res = ascii_strncasecmp(Data, RHS.Data, std::min(Length, RHS.Length)))
+ return Res;
+ if (Length == RHS.Length)
+ return 0;
+ return Length < RHS.Length ? -1 : 1;
+}
+
+/// Check if this string starts with the given \p Prefix, ignoring case.
+bool StringRef::startswith_lower(StringRef Prefix) const noexcept {
+ return Length >= Prefix.Length &&
+ ascii_strncasecmp(Data, Prefix.Data, Prefix.Length) == 0;
+}
+
+/// Check if this string ends with the given \p Suffix, ignoring case.
+bool StringRef::endswith_lower(StringRef Suffix) const noexcept {
+ return Length >= Suffix.Length &&
+ ascii_strncasecmp(end() - Suffix.Length, Suffix.Data, Suffix.Length) == 0;
+}
+
+size_t StringRef::find_lower(char C, size_t From) const noexcept {
+ char L = toLower(C);
+ return find_if([L](char D) { return toLower(D) == L; }, From);
+}
+
+/// compare_numeric - Compare strings, handle embedded numbers.
+int StringRef::compare_numeric(StringRef RHS) const noexcept {
+ for (size_t I = 0, E = std::min(Length, RHS.Length); I != E; ++I) {
+ // Check for sequences of digits.
+ if (isDigit(Data[I]) && isDigit(RHS.Data[I])) {
+ // The longer sequence of numbers is considered larger.
+ // This doesn't really handle prefixed zeros well.
+ size_t J;
+ for (J = I + 1; J != E + 1; ++J) {
+ bool ld = J < Length && isDigit(Data[J]);
+ bool rd = J < RHS.Length && isDigit(RHS.Data[J]);
+ if (ld != rd)
+ return rd ? -1 : 1;
+ if (!rd)
+ break;
+ }
+ // The two number sequences have the same length (J-I), just memcmp them.
+ if (int Res = compareMemory(Data + I, RHS.Data + I, J - I))
+ return Res < 0 ? -1 : 1;
+ // Identical number sequences, continue search after the numbers.
+ I = J - 1;
+ continue;
+ }
+ if (Data[I] != RHS.Data[I])
+ return (unsigned char)Data[I] < (unsigned char)RHS.Data[I] ? -1 : 1;
+ }
+ if (Length == RHS.Length)
+ return 0;
+ return Length < RHS.Length ? -1 : 1;
+}
+
+//===----------------------------------------------------------------------===//
+// String Operations
+//===----------------------------------------------------------------------===//
+
+std::string StringRef::lower() const {
+ std::string Result(size(), char());
+ for (size_type i = 0, e = size(); i != e; ++i) {
+ Result[i] = toLower(Data[i]);
+ }
+ return Result;
+}
+
+std::string StringRef::upper() const {
+ std::string Result(size(), char());
+ for (size_type i = 0, e = size(); i != e; ++i) {
+ Result[i] = toUpper(Data[i]);
+ }
+ return Result;
+}
+
+//===----------------------------------------------------------------------===//
+// String Searching
+//===----------------------------------------------------------------------===//
+
+
+/// find - Search for the first string \arg Str in the string.
+///
+/// \return - The index of the first occurrence of \arg Str, or npos if not
+/// found.
+size_t StringRef::find(StringRef Str, size_t From) const noexcept {
+ if (From > Length)
+ return npos;
+
+ const char *Start = Data + From;
+ size_t Size = Length - From;
+
+ const char *Needle = Str.data();
+ size_t N = Str.size();
+ if (N == 0)
+ return From;
+ if (Size < N)
+ return npos;
+ if (N == 1) {
+ const char *Ptr = (const char *)::memchr(Start, Needle[0], Size);
+ return Ptr == nullptr ? npos : Ptr - Data;
+ }
+
+ const char *Stop = Start + (Size - N + 1);
+
+ // For short haystacks or unsupported needles fall back to the naive algorithm
+ if (Size < 16 || N > 255) {
+ do {
+ if (std::memcmp(Start, Needle, N) == 0)
+ return Start - Data;
+ ++Start;
+ } while (Start < Stop);
+ return npos;
+ }
+
+ // Build the bad char heuristic table, with uint8_t to reduce cache thrashing.
+ uint8_t BadCharSkip[256];
+ std::memset(BadCharSkip, N, 256);
+ for (unsigned i = 0; i != N-1; ++i)
+ BadCharSkip[(uint8_t)Str[i]] = N-1-i;
+
+ do {
+ uint8_t Last = Start[N - 1];
+ if (LLVM_UNLIKELY(Last == (uint8_t)Needle[N - 1]))
+ if (std::memcmp(Start, Needle, N - 1) == 0)
+ return Start - Data;
+
+ // Otherwise skip the appropriate number of bytes.
+ Start += BadCharSkip[Last];
+ } while (Start < Stop);
+
+ return npos;
+}
+
+size_t StringRef::find_lower(StringRef Str, size_t From) const noexcept {
+ StringRef This = substr(From);
+ while (This.size() >= Str.size()) {
+ if (This.startswith_lower(Str))
+ return From;
+ This = This.drop_front();
+ ++From;
+ }
+ return npos;
+}
+
+size_t StringRef::rfind_lower(char C, size_t From) const noexcept {
+ From = std::min(From, Length);
+ size_t i = From;
+ while (i != 0) {
+ --i;
+ if (toLower(Data[i]) == toLower(C))
+ return i;
+ }
+ return npos;
+}
+
+/// rfind - Search for the last string \arg Str in the string.
+///
+/// \return - The index of the last occurrence of \arg Str, or npos if not
+/// found.
+size_t StringRef::rfind(StringRef Str) const noexcept {
+ size_t N = Str.size();
+ if (N > Length)
+ return npos;
+ for (size_t i = Length - N + 1, e = 0; i != e;) {
+ --i;
+ if (substr(i, N).equals(Str))
+ return i;
+ }
+ return npos;
+}
+
+size_t StringRef::rfind_lower(StringRef Str) const noexcept {
+ size_t N = Str.size();
+ if (N > Length)
+ return npos;
+ for (size_t i = Length - N + 1, e = 0; i != e;) {
+ --i;
+ if (substr(i, N).equals_lower(Str))
+ return i;
+ }
+ return npos;
+}
+
+/// find_first_of - Find the first character in the string that is in \arg
+/// Chars, or npos if not found.
+///
+/// Note: O(size() + Chars.size())
+StringRef::size_type StringRef::find_first_of(StringRef Chars,
+ size_t From) const noexcept {
+ std::bitset<1 << CHAR_BIT> CharBits;
+ for (size_type i = 0; i != Chars.size(); ++i)
+ CharBits.set((unsigned char)Chars[i]);
+
+ for (size_type i = std::min(From, Length), e = Length; i != e; ++i)
+ if (CharBits.test((unsigned char)Data[i]))
+ return i;
+ return npos;
+}
+
+/// find_first_not_of - Find the first character in the string that is not
+/// \arg C or npos if not found.
+StringRef::size_type StringRef::find_first_not_of(char C, size_t From) const noexcept {
+ for (size_type i = std::min(From, Length), e = Length; i != e; ++i)
+ if (Data[i] != C)
+ return i;
+ return npos;
+}
+
+/// find_first_not_of - Find the first character in the string that is not
+/// in the string \arg Chars, or npos if not found.
+///
+/// Note: O(size() + Chars.size())
+StringRef::size_type StringRef::find_first_not_of(StringRef Chars,
+ size_t From) const noexcept {
+ std::bitset<1 << CHAR_BIT> CharBits;
+ for (size_type i = 0; i != Chars.size(); ++i)
+ CharBits.set((unsigned char)Chars[i]);
+
+ for (size_type i = std::min(From, Length), e = Length; i != e; ++i)
+ if (!CharBits.test((unsigned char)Data[i]))
+ return i;
+ return npos;
+}
+
+/// find_last_of - Find the last character in the string that is in \arg C,
+/// or npos if not found.
+///
+/// Note: O(size() + Chars.size())
+StringRef::size_type StringRef::find_last_of(StringRef Chars,
+ size_t From) const noexcept {
+ std::bitset<1 << CHAR_BIT> CharBits;
+ for (size_type i = 0; i != Chars.size(); ++i)
+ CharBits.set((unsigned char)Chars[i]);
+
+ for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i)
+ if (CharBits.test((unsigned char)Data[i]))
+ return i;
+ return npos;
+}
+
+/// find_last_not_of - Find the last character in the string that is not
+/// \arg C, or npos if not found.
+StringRef::size_type StringRef::find_last_not_of(char C, size_t From) const noexcept {
+ for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i)
+ if (Data[i] != C)
+ return i;
+ return npos;
+}
+
+/// find_last_not_of - Find the last character in the string that is not in
+/// \arg Chars, or npos if not found.
+///
+/// Note: O(size() + Chars.size())
+StringRef::size_type StringRef::find_last_not_of(StringRef Chars,
+ size_t From) const noexcept {
+ std::bitset<1 << CHAR_BIT> CharBits;
+ for (size_type i = 0, e = Chars.size(); i != e; ++i)
+ CharBits.set((unsigned char)Chars[i]);
+
+ for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i)
+ if (!CharBits.test((unsigned char)Data[i]))
+ return i;
+ return npos;
+}
+
+void StringRef::split(SmallVectorImpl<StringRef> &A,
+ StringRef Separator, int MaxSplit,
+ bool KeepEmpty) const {
+ StringRef S = *this;
+
+ // Count down from MaxSplit. When MaxSplit is -1, this will just split
+ // "forever". This doesn't support splitting more than 2^31 times
+ // intentionally; if we ever want that we can make MaxSplit a 64-bit integer
+ // but that seems unlikely to be useful.
+ while (MaxSplit-- != 0) {
+ size_t Idx = S.find(Separator);
+ if (Idx == npos)
+ break;
+
+ // Push this split.
+ if (KeepEmpty || Idx > 0)
+ A.push_back(S.slice(0, Idx));
+
+ // Jump forward.
+ S = S.slice(Idx + Separator.size(), npos);
+ }
+
+ // Push the tail.
+ if (KeepEmpty || !S.empty())
+ A.push_back(S);
+}
+
+void StringRef::split(SmallVectorImpl<StringRef> &A, char Separator,
+ int MaxSplit, bool KeepEmpty) const {
+ StringRef S = *this;
+
+ // Count down from MaxSplit. When MaxSplit is -1, this will just split
+ // "forever". This doesn't support splitting more than 2^31 times
+ // intentionally; if we ever want that we can make MaxSplit a 64-bit integer
+ // but that seems unlikely to be useful.
+ while (MaxSplit-- != 0) {
+ size_t Idx = S.find(Separator);
+ if (Idx == npos)
+ break;
+
+ // Push this split.
+ if (KeepEmpty || Idx > 0)
+ A.push_back(S.slice(0, Idx));
+
+ // Jump forward.
+ S = S.slice(Idx + 1, npos);
+ }
+
+ // Push the tail.
+ if (KeepEmpty || !S.empty())
+ A.push_back(S);
+}
+
+//===----------------------------------------------------------------------===//
+// Helpful Algorithms
+//===----------------------------------------------------------------------===//
+
+/// count - Return the number of non-overlapped occurrences of \arg Str in
+/// the string.
+size_t StringRef::count(StringRef Str) const noexcept {
+ size_t Count = 0;
+ size_t N = Str.size();
+ if (N > Length)
+ return 0;
+ for (size_t i = 0, e = Length - N + 1; i != e; ++i)
+ if (substr(i, N).equals(Str))
+ ++Count;
+ return Count;
+}
+
+static unsigned GetAutoSenseRadix(StringRef &Str) noexcept {
+ if (Str.empty())
+ return 10;
+
+ if (Str.startswith("0x") || Str.startswith("0X")) {
+ Str = Str.substr(2);
+ return 16;
+ }
+
+ if (Str.startswith("0b") || Str.startswith("0B")) {
+ Str = Str.substr(2);
+ return 2;
+ }
+
+ if (Str.startswith("0o")) {
+ Str = Str.substr(2);
+ return 8;
+ }
+
+ if (Str[0] == '0' && Str.size() > 1 && isDigit(Str[1])) {
+ Str = Str.substr(1);
+ return 8;
+ }
+
+ return 10;
+}
+
+bool wpi::consumeUnsignedInteger(StringRef &Str, unsigned Radix,
+ unsigned long long &Result) noexcept {
+ // Autosense radix if not specified.
+ if (Radix == 0)
+ Radix = GetAutoSenseRadix(Str);
+
+ // Empty strings (after the radix autosense) are invalid.
+ if (Str.empty()) return true;
+
+ // Parse all the bytes of the string given this radix. Watch for overflow.
+ StringRef Str2 = Str;
+ Result = 0;
+ while (!Str2.empty()) {
+ unsigned CharVal;
+ if (Str2[0] >= '0' && Str2[0] <= '9')
+ CharVal = Str2[0] - '0';
+ else if (Str2[0] >= 'a' && Str2[0] <= 'z')
+ CharVal = Str2[0] - 'a' + 10;
+ else if (Str2[0] >= 'A' && Str2[0] <= 'Z')
+ CharVal = Str2[0] - 'A' + 10;
+ else
+ break;
+
+ // If the parsed value is larger than the integer radix, we cannot
+ // consume any more characters.
+ if (CharVal >= Radix)
+ break;
+
+ // Add in this character.
+ unsigned long long PrevResult = Result;
+ Result = Result * Radix + CharVal;
+
+ // Check for overflow by shifting back and seeing if bits were lost.
+ if (Result / Radix < PrevResult)
+ return true;
+
+ Str2 = Str2.substr(1);
+ }
+
+ // We consider the operation a failure if no characters were consumed
+ // successfully.
+ if (Str.size() == Str2.size())
+ return true;
+
+ Str = Str2;
+ return false;
+}
+
+bool wpi::consumeSignedInteger(StringRef &Str, unsigned Radix,
+ long long &Result) noexcept {
+ unsigned long long ULLVal;
+
+ // Handle positive strings first.
+ if (Str.empty() || Str.front() != '-') {
+ if (consumeUnsignedInteger(Str, Radix, ULLVal) ||
+ // Check for value so large it overflows a signed value.
+ (long long)ULLVal < 0)
+ return true;
+ Result = ULLVal;
+ return false;
+ }
+
+ // Get the positive part of the value.
+ StringRef Str2 = Str.drop_front(1);
+ if (consumeUnsignedInteger(Str2, Radix, ULLVal) ||
+ // Reject values so large they'd overflow as negative signed, but allow
+ // "-0". This negates the unsigned so that the negative isn't undefined
+ // on signed overflow.
+ (long long)-ULLVal > 0)
+ return true;
+
+ Str = Str2;
+ Result = -ULLVal;
+ return false;
+}
+
+/// GetAsUnsignedInteger - Workhorse method that converts a integer character
+/// sequence of radix up to 36 to an unsigned long long value.
+bool wpi::getAsUnsignedInteger(StringRef Str, unsigned Radix,
+ unsigned long long &Result) noexcept {
+ if (consumeUnsignedInteger(Str, Radix, Result))
+ return true;
+
+ // For getAsUnsignedInteger, we require the whole string to be consumed or
+ // else we consider it a failure.
+ return !Str.empty();
+}
+
+bool wpi::getAsSignedInteger(StringRef Str, unsigned Radix,
+ long long &Result) noexcept {
+ if (consumeSignedInteger(Str, Radix, Result))
+ return true;
+
+ // For getAsSignedInteger, we require the whole string to be consumed or else
+ // we consider it a failure.
+ return !Str.empty();
+}
+
+std::ostream &wpi::operator<<(std::ostream &os, StringRef string) {
+ os.write(string.data(), string.size());
+ return os;
+}
+
+// Implementation of StringRef hashing.
+hash_code wpi::hash_value(StringRef S) {
+ return hash_combine_range(S.begin(), S.end());
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/Twine.cpp b/wpiutil/src/main/native/cpp/llvm/Twine.cpp
new file mode 100644
index 0000000..ac705ff
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/Twine.cpp
@@ -0,0 +1,169 @@
+//===-- Twine.cpp - Fast Temporary String Concatenation -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/Twine.h"
+#include "wpi/SmallString.h"
+#include "wpi/raw_ostream.h"
+using namespace wpi;
+
+std::string Twine::str() const {
+ // If we're storing only a std::string, just return it.
+ if (LHSKind == StdStringKind && RHSKind == EmptyKind)
+ return *LHS.stdString;
+
+ // Otherwise, flatten and copy the contents first.
+ SmallString<256> Vec;
+ return toStringRef(Vec).str();
+}
+
+void Twine::toVector(SmallVectorImpl<char> &Out) const {
+ raw_svector_ostream OS(Out);
+ print(OS);
+}
+
+StringRef Twine::toNullTerminatedStringRef(SmallVectorImpl<char> &Out) const {
+ if (isUnary()) {
+ switch (getLHSKind()) {
+ case CStringKind:
+ // Already null terminated, yay!
+ return StringRef(LHS.cString);
+ case StdStringKind: {
+ const std::string *str = LHS.stdString;
+ return StringRef(str->c_str(), str->size());
+ }
+ default:
+ break;
+ }
+ }
+ toVector(Out);
+ Out.push_back(0);
+ Out.pop_back();
+ return StringRef(Out.data(), Out.size());
+}
+
+void Twine::printOneChild(raw_ostream &OS, Child Ptr,
+ NodeKind Kind) const {
+ switch (Kind) {
+ case Twine::NullKind: break;
+ case Twine::EmptyKind: break;
+ case Twine::TwineKind:
+ Ptr.twine->print(OS);
+ break;
+ case Twine::CStringKind:
+ OS << Ptr.cString;
+ break;
+ case Twine::StdStringKind:
+ OS << *Ptr.stdString;
+ break;
+ case Twine::StringRefKind:
+ OS << *Ptr.stringRef;
+ break;
+ case Twine::SmallStringKind:
+ OS << *Ptr.smallString;
+ break;
+ case Twine::CharKind:
+ OS << Ptr.character;
+ break;
+ case Twine::DecUIKind:
+ OS << Ptr.decUI;
+ break;
+ case Twine::DecIKind:
+ OS << Ptr.decI;
+ break;
+ case Twine::DecULKind:
+ OS << *Ptr.decUL;
+ break;
+ case Twine::DecLKind:
+ OS << *Ptr.decL;
+ break;
+ case Twine::DecULLKind:
+ OS << *Ptr.decULL;
+ break;
+ case Twine::DecLLKind:
+ OS << *Ptr.decLL;
+ break;
+ case Twine::UHexKind:
+ OS.write_hex(*Ptr.uHex);
+ break;
+ }
+}
+
+void Twine::printOneChildRepr(raw_ostream &OS, Child Ptr,
+ NodeKind Kind) const {
+ switch (Kind) {
+ case Twine::NullKind:
+ OS << "null"; break;
+ case Twine::EmptyKind:
+ OS << "empty"; break;
+ case Twine::TwineKind:
+ OS << "rope:";
+ Ptr.twine->printRepr(OS);
+ break;
+ case Twine::CStringKind:
+ OS << "cstring:\""
+ << Ptr.cString << "\"";
+ break;
+ case Twine::StdStringKind:
+ OS << "std::string:\""
+ << Ptr.stdString << "\"";
+ break;
+ case Twine::StringRefKind:
+ OS << "stringref:\""
+ << Ptr.stringRef << "\"";
+ break;
+ case Twine::SmallStringKind:
+ OS << "smallstring:\"" << *Ptr.smallString << "\"";
+ break;
+ case Twine::CharKind:
+ OS << "char:\"" << Ptr.character << "\"";
+ break;
+ case Twine::DecUIKind:
+ OS << "decUI:\"" << Ptr.decUI << "\"";
+ break;
+ case Twine::DecIKind:
+ OS << "decI:\"" << Ptr.decI << "\"";
+ break;
+ case Twine::DecULKind:
+ OS << "decUL:\"" << *Ptr.decUL << "\"";
+ break;
+ case Twine::DecLKind:
+ OS << "decL:\"" << *Ptr.decL << "\"";
+ break;
+ case Twine::DecULLKind:
+ OS << "decULL:\"" << *Ptr.decULL << "\"";
+ break;
+ case Twine::DecLLKind:
+ OS << "decLL:\"" << *Ptr.decLL << "\"";
+ break;
+ case Twine::UHexKind:
+ OS << "uhex:\"" << Ptr.uHex << "\"";
+ break;
+ }
+}
+
+void Twine::print(raw_ostream &OS) const {
+ printOneChild(OS, LHS, getLHSKind());
+ printOneChild(OS, RHS, getRHSKind());
+}
+
+void Twine::printRepr(raw_ostream &OS) const {
+ OS << "(Twine ";
+ printOneChildRepr(OS, LHS, getLHSKind());
+ OS << " ";
+ printOneChildRepr(OS, RHS, getRHSKind());
+ OS << ")";
+}
+
+LLVM_DUMP_METHOD void Twine::dump() const {
+ print(errs());
+}
+
+LLVM_DUMP_METHOD void Twine::dumpRepr() const {
+ printRepr(errs());
+}
diff --git a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc
new file mode 100644
index 0000000..b786813
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc
@@ -0,0 +1,539 @@
+//===- llvm/Support/Unix/Path.inc - Unix Path Implementation ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Unix specific implementation of the Path API.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+//=== WARNING: Implementation here must contain only generic UNIX code that
+//=== is guaranteed to work on *all* UNIX variants.
+//===----------------------------------------------------------------------===//
+
+#include "wpi/Errno.h"
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+using namespace wpi;
+
+namespace wpi {
+namespace sys {
+namespace fs {
+
+const file_t kInvalidFile = -1;
+
+TimePoint<> basic_file_status::getLastAccessedTime() const {
+ return toTimePoint(fs_st_atime, fs_st_atime_nsec);
+}
+
+TimePoint<> basic_file_status::getLastModificationTime() const {
+ return toTimePoint(fs_st_mtime, fs_st_mtime_nsec);
+}
+
+UniqueID file_status::getUniqueID() const {
+ return UniqueID(fs_st_dev, fs_st_ino);
+}
+
+uint32_t file_status::getLinkCount() const {
+ return fs_st_nlinks;
+}
+
+std::error_code current_path(SmallVectorImpl<char> &result) {
+ result.clear();
+
+ const char *pwd = ::getenv("PWD");
+ wpi::sys::fs::file_status PWDStatus, DotStatus;
+ if (pwd && wpi::sys::path::is_absolute(pwd) &&
+ !wpi::sys::fs::status(pwd, PWDStatus) &&
+ !wpi::sys::fs::status(".", DotStatus) &&
+ PWDStatus.getUniqueID() == DotStatus.getUniqueID()) {
+ result.append(pwd, pwd + strlen(pwd));
+ return std::error_code();
+ }
+
+#ifdef MAXPATHLEN
+ result.reserve(MAXPATHLEN);
+#else
+ result.reserve(1024);
+#endif
+
+ while (true) {
+ if (::getcwd(result.data(), result.capacity()) == nullptr) {
+ // See if there was a real error.
+ if (errno != ENOMEM)
+ return std::error_code(errno, std::generic_category());
+ // Otherwise there just wasn't enough space.
+ result.reserve(result.capacity() * 2);
+ } else
+ break;
+ }
+
+ result.set_size(strlen(result.data()));
+ return std::error_code();
+}
+
+static int convertAccessMode(AccessMode Mode) {
+ switch (Mode) {
+ case AccessMode::Exist:
+ return F_OK;
+ case AccessMode::Write:
+ return W_OK;
+ case AccessMode::Execute:
+ return R_OK | X_OK; // scripts also need R_OK.
+ default:
+ return F_OK;
+ }
+}
+
+std::error_code access(const Twine &Path, AccessMode Mode) {
+ SmallString<128> PathStorage;
+ StringRef P = Path.toNullTerminatedStringRef(PathStorage);
+
+ if (::access(P.begin(), convertAccessMode(Mode)) == -1)
+ return std::error_code(errno, std::generic_category());
+
+ if (Mode == AccessMode::Execute) {
+ // Don't say that directories are executable.
+ struct stat buf;
+ if (0 != stat(P.begin(), &buf))
+ return errc::permission_denied;
+ if (!S_ISREG(buf.st_mode))
+ return errc::permission_denied;
+ }
+
+ return std::error_code();
+}
+
+bool equivalent(file_status A, file_status B) {
+ assert(status_known(A) && status_known(B));
+ return A.fs_st_dev == B.fs_st_dev &&
+ A.fs_st_ino == B.fs_st_ino;
+}
+
+std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
+ file_status fsA, fsB;
+ if (std::error_code ec = status(A, fsA))
+ return ec;
+ if (std::error_code ec = status(B, fsB))
+ return ec;
+ result = equivalent(fsA, fsB);
+ return std::error_code();
+}
+
+static file_type typeForMode(mode_t Mode) {
+ if (S_ISDIR(Mode))
+ return file_type::directory_file;
+ else if (S_ISREG(Mode))
+ return file_type::regular_file;
+ else if (S_ISBLK(Mode))
+ return file_type::block_file;
+ else if (S_ISCHR(Mode))
+ return file_type::character_file;
+ else if (S_ISFIFO(Mode))
+ return file_type::fifo_file;
+ else if (S_ISSOCK(Mode))
+ return file_type::socket_file;
+ else if (S_ISLNK(Mode))
+ return file_type::symlink_file;
+ return file_type::type_unknown;
+}
+
+static std::error_code fillStatus(int StatRet, const struct stat &Status,
+ file_status &Result) {
+ if (StatRet != 0) {
+ std::error_code EC(errno, std::generic_category());
+ if (EC == errc::no_such_file_or_directory)
+ Result = file_status(file_type::file_not_found);
+ else
+ Result = file_status(file_type::status_error);
+ return EC;
+ }
+
+ uint32_t atime_nsec, mtime_nsec;
+#if defined(__APPLE__)
+ atime_nsec = Status.st_atimespec.tv_nsec;
+ mtime_nsec = Status.st_mtimespec.tv_nsec;
+#else
+ atime_nsec = Status.st_atim.tv_nsec;
+ mtime_nsec = Status.st_mtim.tv_nsec;
+#endif
+
+ perms Perms = static_cast<perms>(Status.st_mode) & all_perms;
+ Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev,
+ Status.st_nlink, Status.st_ino,
+ Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec,
+ Status.st_uid, Status.st_gid, Status.st_size);
+
+ return std::error_code();
+}
+
+std::error_code status(const Twine &Path, file_status &Result, bool Follow) {
+ SmallString<128> PathStorage;
+ StringRef P = Path.toNullTerminatedStringRef(PathStorage);
+
+ struct stat Status;
+ int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status);
+ return fillStatus(StatRet, Status, Result);
+}
+
+std::error_code status(int FD, file_status &Result) {
+ struct stat Status;
+ int StatRet = ::fstat(FD, &Status);
+ return fillStatus(StatRet, Status, Result);
+}
+
+std::error_code mapped_file_region::init(int FD, uint64_t Offset,
+ mapmode Mode) {
+ assert(Size != 0);
+
+ int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
+ int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
+#if defined(__APPLE__)
+ //----------------------------------------------------------------------
+ // Newer versions of MacOSX have a flag that will allow us to read from
+ // binaries whose code signature is invalid without crashing by using
+ // the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media
+ // is mapped we can avoid crashing and return zeroes to any pages we try
+ // to read if the media becomes unavailable by using the
+ // MAP_RESILIENT_MEDIA flag. These flags are only usable when mapping
+ // with PROT_READ, so take care not to specify them otherwise.
+ //----------------------------------------------------------------------
+ if (Mode == readonly) {
+#if defined(MAP_RESILIENT_CODESIGN)
+ flags |= MAP_RESILIENT_CODESIGN;
+#endif
+#if defined(MAP_RESILIENT_MEDIA)
+ flags |= MAP_RESILIENT_MEDIA;
+#endif
+ }
+#endif // #if defined (__APPLE__)
+
+ Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset);
+ if (Mapping == MAP_FAILED)
+ return std::error_code(errno, std::generic_category());
+ return std::error_code();
+}
+
+mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
+ uint64_t offset, std::error_code &ec)
+ : Size(length), Mapping(), Mode(mode) {
+ (void)Mode;
+ ec = init(fd, offset, mode);
+ if (ec)
+ Mapping = nullptr;
+}
+
+mapped_file_region::~mapped_file_region() {
+ if (Mapping)
+ ::munmap(Mapping, Size);
+}
+
+size_t mapped_file_region::size() const {
+ assert(Mapping && "Mapping failed but used anyway!");
+ return Size;
+}
+
+char *mapped_file_region::data() const {
+ assert(Mapping && "Mapping failed but used anyway!");
+ return reinterpret_cast<char*>(Mapping);
+}
+
+const char *mapped_file_region::const_data() const {
+ assert(Mapping && "Mapping failed but used anyway!");
+ return reinterpret_cast<const char*>(Mapping);
+}
+
+int mapped_file_region::alignment() {
+ return ::sysconf(_SC_PAGE_SIZE);
+}
+
+std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
+ StringRef path,
+ bool follow_symlinks) {
+ SmallString<128> path_null(path);
+ DIR *directory = ::opendir(path_null.c_str());
+ if (!directory)
+ return std::error_code(errno, std::generic_category());
+
+ it.IterationHandle = reinterpret_cast<intptr_t>(directory);
+ // Add something for replace_filename to replace.
+ path::append(path_null, ".");
+ it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks);
+ return directory_iterator_increment(it);
+}
+
+std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
+ if (it.IterationHandle)
+ ::closedir(reinterpret_cast<DIR *>(it.IterationHandle));
+ it.IterationHandle = 0;
+ it.CurrentEntry = directory_entry();
+ return std::error_code();
+}
+
+static file_type direntType(dirent* Entry) {
+ // Most platforms provide the file type in the dirent: Linux/BSD/Mac.
+ // The DTTOIF macro lets us reuse our status -> type conversion.
+ return typeForMode(DTTOIF(Entry->d_type));
+}
+
+std::error_code detail::directory_iterator_increment(detail::DirIterState &It) {
+ errno = 0;
+ dirent *CurDir = ::readdir(reinterpret_cast<DIR *>(It.IterationHandle));
+ if (CurDir == nullptr && errno != 0) {
+ return std::error_code(errno, std::generic_category());
+ } else if (CurDir != nullptr) {
+ StringRef Name(CurDir->d_name);
+ if ((Name.size() == 1 && Name[0] == '.') ||
+ (Name.size() == 2 && Name[0] == '.' && Name[1] == '.'))
+ return directory_iterator_increment(It);
+ It.CurrentEntry.replace_filename(Name, direntType(CurDir));
+ } else
+ return directory_iterator_destruct(It);
+
+ return std::error_code();
+}
+
+ErrorOr<basic_file_status> directory_entry::status() const {
+ file_status s;
+ if (auto EC = fs::status(Path, s, FollowSymlinks))
+ return EC;
+ return s;
+}
+
+#if !defined(F_GETPATH)
+static bool hasProcSelfFD() {
+ // If we have a /proc filesystem mounted, we can quickly establish the
+ // real name of the file with readlink
+ static const bool Result = (::access("/proc/self/fd", R_OK) == 0);
+ return Result;
+}
+#endif
+
+static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
+ FileAccess Access) {
+ int Result = 0;
+ if (Access == FA_Read)
+ Result |= O_RDONLY;
+ else if (Access == FA_Write)
+ Result |= O_WRONLY;
+ else if (Access == (FA_Read | FA_Write))
+ Result |= O_RDWR;
+
+ // This is for compatibility with old code that assumed F_Append implied
+ // would open an existing file. See Windows/Path.inc for a longer comment.
+ if (Flags & F_Append)
+ Disp = CD_OpenAlways;
+
+ if (Disp == CD_CreateNew) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ Result |= O_EXCL; // Fail if it does.
+ } else if (Disp == CD_CreateAlways) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ Result |= O_TRUNC; // Truncate if it does.
+ } else if (Disp == CD_OpenAlways) {
+ Result |= O_CREAT; // Create if it doesn't exist.
+ } else if (Disp == CD_OpenExisting) {
+ // Nothing special, just don't add O_CREAT and we get these semantics.
+ }
+
+ if (Flags & F_Append)
+ Result |= O_APPEND;
+
+#ifdef O_CLOEXEC
+ if (!(Flags & OF_ChildInherit))
+ Result |= O_CLOEXEC;
+#endif
+
+ return Result;
+}
+
+std::error_code openFile(const Twine &Name, int &ResultFD,
+ CreationDisposition Disp, FileAccess Access,
+ OpenFlags Flags, unsigned Mode) {
+ int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
+
+ SmallString<128> Storage;
+ StringRef P = Name.toNullTerminatedStringRef(Storage);
+ // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
+ // when open is overloaded, such as in Bionic.
+ auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); };
+ if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0)
+ return std::error_code(errno, std::generic_category());
+#ifndef O_CLOEXEC
+ if (!(Flags & OF_ChildInherit)) {
+ int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
+ (void)r;
+ assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
+ }
+#endif
+ return std::error_code();
+}
+
+Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
+ FileAccess Access, OpenFlags Flags,
+ unsigned Mode) {
+
+ int FD;
+ std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode);
+ if (EC)
+ return errorCodeToError(EC);
+ return FD;
+}
+
+std::error_code openFileForRead(const Twine &Name, int &ResultFD,
+ OpenFlags Flags,
+ SmallVectorImpl<char> *RealPath) {
+ std::error_code EC =
+ openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666);
+ if (EC)
+ return EC;
+
+ // Attempt to get the real name of the file, if the user asked
+ if(!RealPath)
+ return std::error_code();
+ RealPath->clear();
+#if defined(F_GETPATH)
+ // When F_GETPATH is availble, it is the quickest way to get
+ // the real path name.
+ char Buffer[MAXPATHLEN];
+ if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1)
+ RealPath->append(Buffer, Buffer + strlen(Buffer));
+#else
+ char Buffer[PATH_MAX];
+ if (hasProcSelfFD()) {
+ char ProcPath[64];
+ snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD);
+ ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer));
+ if (CharCount > 0)
+ RealPath->append(Buffer, Buffer + CharCount);
+ } else {
+ SmallString<128> Storage;
+ StringRef P = Name.toNullTerminatedStringRef(Storage);
+
+ // Use ::realpath to get the real path name
+ if (::realpath(P.begin(), Buffer) != nullptr)
+ RealPath->append(Buffer, Buffer + strlen(Buffer));
+ }
+#endif
+ return std::error_code();
+}
+
+Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
+ SmallVectorImpl<char> *RealPath) {
+ file_t ResultFD;
+ std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath);
+ if (EC)
+ return errorCodeToError(EC);
+ return ResultFD;
+}
+
+void closeFile(file_t &F) {
+ ::close(F);
+ F = kInvalidFile;
+}
+
+} // end namespace fs
+
+namespace path {
+
+bool home_directory(SmallVectorImpl<char> &result) {
+ char *RequestedDir = getenv("HOME");
+ if (!RequestedDir) {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw && pw->pw_dir)
+ RequestedDir = pw->pw_dir;
+ }
+ if (!RequestedDir)
+ return false;
+
+ result.clear();
+ result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
+ return true;
+}
+
+static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) {
+ #if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR)
+ // On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR.
+ // macros defined in <unistd.h> on darwin >= 9
+ int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR
+ : _CS_DARWIN_USER_CACHE_DIR;
+ size_t ConfLen = confstr(ConfName, nullptr, 0);
+ if (ConfLen > 0) {
+ do {
+ Result.resize(ConfLen);
+ ConfLen = confstr(ConfName, Result.data(), Result.size());
+ } while (ConfLen > 0 && ConfLen != Result.size());
+
+ if (ConfLen > 0) {
+ assert(Result.back() == 0);
+ Result.pop_back();
+ return true;
+ }
+
+ Result.clear();
+ }
+ #endif
+ return false;
+}
+
+static const char *getEnvTempDir() {
+ // Check whether the temporary directory is specified by an environment
+ // variable.
+ const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
+ for (const char *Env : EnvironmentVariables) {
+ if (const char *Dir = std::getenv(Env))
+ return Dir;
+ }
+
+ return nullptr;
+}
+
+static const char *getDefaultTempDir(bool ErasedOnReboot) {
+#ifdef P_tmpdir
+ if ((bool)P_tmpdir)
+ return P_tmpdir;
+#endif
+
+ if (ErasedOnReboot)
+ return "/tmp";
+ return "/var/tmp";
+}
+
+void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
+ Result.clear();
+
+ if (ErasedOnReboot) {
+ // There is no env variable for the cache directory.
+ if (const char *RequestedDir = getEnvTempDir()) {
+ Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
+ return;
+ }
+ }
+
+ if (getDarwinConfDir(ErasedOnReboot, Result))
+ return;
+
+ const char *RequestedDir = getDefaultTempDir(ErasedOnReboot);
+ Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
+}
+
+} // end namespace path
+} // end namespace sys
+} // end namespace wpi
diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc
new file mode 100644
index 0000000..e9b5280
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc
@@ -0,0 +1,971 @@
+//===- llvm/Support/Windows/Path.inc - Windows Path Impl --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Windows specific implementation of the Path API.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+//=== WARNING: Implementation here must contain only generic Windows code that
+//=== is guaranteed to work on *all* Windows variants.
+//===----------------------------------------------------------------------===//
+
+#include "wpi/STLExtras.h"
+#include "wpi/ConvertUTF.h"
+#include "wpi/WindowsError.h"
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+// These two headers must be included last, and make sure shlobj is required
+// after Windows.h to make sure it picks up our definition of _WIN32_WINNT
+#include "WindowsSupport.h"
+#include <shellapi.h>
+#include <shlobj.h>
+
+#undef max
+
+// MinGW doesn't define this.
+#ifndef _ERRNO_T_DEFINED
+#define _ERRNO_T_DEFINED
+typedef int errno_t;
+#endif
+
+#ifdef _MSC_VER
+# pragma comment(lib, "shell32.lib")
+# pragma comment(lib, "ole32.lib")
+#pragma warning(push)
+#pragma warning(disable: 4244 4267 4146)
+#endif
+
+using namespace wpi;
+
+using wpi::sys::windows::UTF8ToUTF16;
+using wpi::sys::windows::CurCPToUTF16;
+using wpi::sys::windows::UTF16ToUTF8;
+using wpi::sys::path::widenPath;
+
+static bool is_separator(const wchar_t value) {
+ switch (value) {
+ case L'\\':
+ case L'/':
+ return true;
+ default:
+ return false;
+ }
+}
+
+namespace wpi {
+namespace sys {
+namespace path {
+
+// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the
+// path is longer than CreateDirectory can tolerate, make it absolute and
+// prefixed by '\\?\'.
+std::error_code widenPath(const Twine &Path8,
+ SmallVectorImpl<wchar_t> &Path16) {
+ const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename.
+
+ // Several operations would convert Path8 to SmallString; more efficient to
+ // do it once up front.
+ SmallString<128> Path8Str;
+ Path8.toVector(Path8Str);
+
+ // If we made this path absolute, how much longer would it get?
+ size_t CurPathLen;
+ if (wpi::sys::path::is_absolute(Twine(Path8Str)))
+ CurPathLen = 0; // No contribution from current_path needed.
+ else {
+ CurPathLen = ::GetCurrentDirectoryW(0, NULL);
+ if (CurPathLen == 0)
+ return mapWindowsError(::GetLastError());
+ }
+
+ // Would the absolute path be longer than our limit?
+ if ((Path8Str.size() + CurPathLen) >= MaxDirLen &&
+ !Path8Str.startswith("\\\\?\\")) {
+ SmallString<2*MAX_PATH> FullPath("\\\\?\\");
+ if (CurPathLen) {
+ SmallString<80> CurPath;
+ if (std::error_code EC = wpi::sys::fs::current_path(CurPath))
+ return EC;
+ FullPath.append(CurPath);
+ }
+ // Traverse the requested path, canonicalizing . and .. (because the \\?\
+ // prefix is documented to treat them as real components). Ignore
+ // separators, which can be returned from the iterator if the path has a
+ // drive name. We don't need to call native() on the result since append()
+ // always attaches preferred_separator.
+ for (wpi::sys::path::const_iterator I = wpi::sys::path::begin(Path8Str),
+ E = wpi::sys::path::end(Path8Str);
+ I != E; ++I) {
+ if (I->size() == 1 && is_separator((*I)[0]))
+ continue;
+ if (I->size() == 1 && *I == ".")
+ continue;
+ if (I->size() == 2 && *I == "..")
+ wpi::sys::path::remove_filename(FullPath);
+ else
+ wpi::sys::path::append(FullPath, *I);
+ }
+ return UTF8ToUTF16(FullPath, Path16);
+ }
+
+ // Just use the caller's original path.
+ return UTF8ToUTF16(Path8Str, Path16);
+}
+} // end namespace path
+
+namespace fs {
+
+const file_t kInvalidFile = INVALID_HANDLE_VALUE;
+
+UniqueID file_status::getUniqueID() const {
+ // The file is uniquely identified by the volume serial number along
+ // with the 64-bit file identifier.
+ uint64_t FileID = (static_cast<uint64_t>(FileIndexHigh) << 32ULL) |
+ static_cast<uint64_t>(FileIndexLow);
+
+ return UniqueID(VolumeSerialNumber, FileID);
+}
+
+TimePoint<> basic_file_status::getLastAccessedTime() const {
+ FILETIME Time;
+ Time.dwLowDateTime = LastAccessedTimeLow;
+ Time.dwHighDateTime = LastAccessedTimeHigh;
+ return toTimePoint(Time);
+}
+
+TimePoint<> basic_file_status::getLastModificationTime() const {
+ FILETIME Time;
+ Time.dwLowDateTime = LastWriteTimeLow;
+ Time.dwHighDateTime = LastWriteTimeHigh;
+ return toTimePoint(Time);
+}
+
+uint32_t file_status::getLinkCount() const {
+ return NumLinks;
+}
+
+std::error_code current_path(SmallVectorImpl<char> &result) {
+ SmallVector<wchar_t, MAX_PATH> cur_path;
+ DWORD len = MAX_PATH;
+
+ do {
+ cur_path.reserve(len);
+ len = ::GetCurrentDirectoryW(cur_path.capacity(), cur_path.data());
+
+ // A zero return value indicates a failure other than insufficient space.
+ if (len == 0)
+ return mapWindowsError(::GetLastError());
+
+ // If there's insufficient space, the len returned is larger than the len
+ // given.
+ } while (len > cur_path.capacity());
+
+ // On success, GetCurrentDirectoryW returns the number of characters not
+ // including the null-terminator.
+ cur_path.set_size(len);
+ return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result);
+}
+
+static std::error_code realPathFromHandle(HANDLE H,
+ SmallVectorImpl<wchar_t> &Buffer) {
+ DWORD CountChars = ::GetFinalPathNameByHandleW(
+ H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
+ if (CountChars > Buffer.capacity()) {
+ // The buffer wasn't big enough, try again. In this case the return value
+ // *does* indicate the size of the null terminator.
+ Buffer.reserve(CountChars);
+ CountChars = ::GetFinalPathNameByHandleW(
+ H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
+ }
+ if (CountChars == 0)
+ return mapWindowsError(GetLastError());
+ Buffer.set_size(CountChars);
+ return std::error_code();
+}
+
+static std::error_code realPathFromHandle(HANDLE H,
+ SmallVectorImpl<char> &RealPath) {
+ RealPath.clear();
+ SmallVector<wchar_t, MAX_PATH> Buffer;
+ if (std::error_code EC = realPathFromHandle(H, Buffer))
+ return EC;
+
+ const wchar_t *Data = Buffer.data();
+ DWORD CountChars = Buffer.size();
+ if (CountChars >= 4) {
+ if (0 == ::memcmp(Data, L"\\\\?\\", 8)) {
+ CountChars -= 4;
+ Data += 4;
+ }
+ }
+
+ // Convert the result from UTF-16 to UTF-8.
+ return UTF16ToUTF8(Data, CountChars, RealPath);
+}
+
+static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) {
+ FILE_DISPOSITION_INFO Disposition;
+ Disposition.DeleteFile = Delete;
+ if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
+ sizeof(Disposition)))
+ return mapWindowsError(::GetLastError());
+ return std::error_code();
+}
+
+std::error_code access(const Twine &Path, AccessMode Mode) {
+ SmallVector<wchar_t, 128> PathUtf16;
+
+ if (std::error_code EC = widenPath(Path, PathUtf16))
+ return EC;
+
+ DWORD Attributes = ::GetFileAttributesW(PathUtf16.begin());
+
+ if (Attributes == INVALID_FILE_ATTRIBUTES) {
+ // See if the file didn't actually exist.
+ DWORD LastError = ::GetLastError();
+ if (LastError != ERROR_FILE_NOT_FOUND &&
+ LastError != ERROR_PATH_NOT_FOUND)
+ return mapWindowsError(LastError);
+ return errc::no_such_file_or_directory;
+ }
+
+ if (Mode == AccessMode::Write && (Attributes & FILE_ATTRIBUTE_READONLY))
+ return errc::permission_denied;
+
+ return std::error_code();
+}
+
+bool equivalent(file_status A, file_status B) {
+ assert(status_known(A) && status_known(B));
+ return A.FileIndexHigh == B.FileIndexHigh &&
+ A.FileIndexLow == B.FileIndexLow &&
+ A.FileSizeHigh == B.FileSizeHigh &&
+ A.FileSizeLow == B.FileSizeLow &&
+ A.LastAccessedTimeHigh == B.LastAccessedTimeHigh &&
+ A.LastAccessedTimeLow == B.LastAccessedTimeLow &&
+ A.LastWriteTimeHigh == B.LastWriteTimeHigh &&
+ A.LastWriteTimeLow == B.LastWriteTimeLow &&
+ A.VolumeSerialNumber == B.VolumeSerialNumber;
+}
+
+std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
+ file_status fsA, fsB;
+ if (std::error_code ec = status(A, fsA))
+ return ec;
+ if (std::error_code ec = status(B, fsB))
+ return ec;
+ result = equivalent(fsA, fsB);
+ return std::error_code();
+}
+
+static bool isReservedName(StringRef path) {
+ // This list of reserved names comes from MSDN, at:
+ // http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
+ static const char *const sReservedNames[] = { "nul", "con", "prn", "aux",
+ "com1", "com2", "com3", "com4",
+ "com5", "com6", "com7", "com8",
+ "com9", "lpt1", "lpt2", "lpt3",
+ "lpt4", "lpt5", "lpt6", "lpt7",
+ "lpt8", "lpt9" };
+
+ // First, check to see if this is a device namespace, which always
+ // starts with \\.\, since device namespaces are not legal file paths.
+ if (path.startswith("\\\\.\\"))
+ return true;
+
+ // Then compare against the list of ancient reserved names.
+ for (size_t i = 0; i < array_lengthof(sReservedNames); ++i) {
+ if (path.equals_lower(sReservedNames[i]))
+ return true;
+ }
+
+ // The path isn't what we consider reserved.
+ return false;
+}
+
+static file_type file_type_from_attrs(DWORD Attrs) {
+ return (Attrs & FILE_ATTRIBUTE_DIRECTORY) ? file_type::directory_file
+ : file_type::regular_file;
+}
+
+static perms perms_from_attrs(DWORD Attrs) {
+ return (Attrs & FILE_ATTRIBUTE_READONLY) ? (all_read | all_exe) : all_all;
+}
+
+static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
+ if (FileHandle == INVALID_HANDLE_VALUE)
+ goto handle_status_error;
+
+ switch (::GetFileType(FileHandle)) {
+ default:
+ Result = file_status(file_type::type_unknown);
+ return std::error_code();
+ case FILE_TYPE_UNKNOWN: {
+ DWORD Err = ::GetLastError();
+ if (Err != NO_ERROR)
+ return mapWindowsError(Err);
+ Result = file_status(file_type::type_unknown);
+ return std::error_code();
+ }
+ case FILE_TYPE_DISK:
+ break;
+ case FILE_TYPE_CHAR:
+ Result = file_status(file_type::character_file);
+ return std::error_code();
+ case FILE_TYPE_PIPE:
+ Result = file_status(file_type::fifo_file);
+ return std::error_code();
+ }
+
+ BY_HANDLE_FILE_INFORMATION Info;
+ if (!::GetFileInformationByHandle(FileHandle, &Info))
+ goto handle_status_error;
+
+ Result = file_status(
+ file_type_from_attrs(Info.dwFileAttributes),
+ perms_from_attrs(Info.dwFileAttributes), Info.nNumberOfLinks,
+ Info.ftLastAccessTime.dwHighDateTime, Info.ftLastAccessTime.dwLowDateTime,
+ Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime,
+ Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow,
+ Info.nFileIndexHigh, Info.nFileIndexLow);
+ return std::error_code();
+
+handle_status_error:
+ DWORD LastError = ::GetLastError();
+ if (LastError == ERROR_FILE_NOT_FOUND ||
+ LastError == ERROR_PATH_NOT_FOUND)
+ Result = file_status(file_type::file_not_found);
+ else if (LastError == ERROR_SHARING_VIOLATION)
+ Result = file_status(file_type::type_unknown);
+ else
+ Result = file_status(file_type::status_error);
+ return mapWindowsError(LastError);
+}
+
+std::error_code status(const Twine &path, file_status &result, bool Follow) {
+ SmallString<128> path_storage;
+ SmallVector<wchar_t, 128> path_utf16;
+
+ StringRef path8 = path.toStringRef(path_storage);
+ if (isReservedName(path8)) {
+ result = file_status(file_type::character_file);
+ return std::error_code();
+ }
+
+ if (std::error_code ec = widenPath(path8, path_utf16))
+ return ec;
+
+ DWORD attr = ::GetFileAttributesW(path_utf16.begin());
+ if (attr == INVALID_FILE_ATTRIBUTES)
+ return getStatus(INVALID_HANDLE_VALUE, result);
+
+ DWORD Flags = FILE_FLAG_BACKUP_SEMANTICS;
+ // Handle reparse points.
+ if (!Follow && (attr & FILE_ATTRIBUTE_REPARSE_POINT))
+ Flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+ ScopedFileHandle h(
+ ::CreateFileW(path_utf16.begin(), 0, // Attributes only.
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, Flags, 0));
+ if (!h)
+ return getStatus(INVALID_HANDLE_VALUE, result);
+
+ return getStatus(h, result);
+}
+
+std::error_code status(int FD, file_status &Result) {
+ HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
+ return getStatus(FileHandle, Result);
+}
+
+std::error_code mapped_file_region::init(int FD, uint64_t Offset,
+ mapmode Mode) {
+ this->Mode = Mode;
+ HANDLE OrigFileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
+ if (OrigFileHandle == INVALID_HANDLE_VALUE)
+ return make_error_code(errc::bad_file_descriptor);
+
+ DWORD flprotect;
+ switch (Mode) {
+ case readonly: flprotect = PAGE_READONLY; break;
+ case readwrite: flprotect = PAGE_READWRITE; break;
+ case priv: flprotect = PAGE_WRITECOPY; break;
+ }
+
+ HANDLE FileMappingHandle =
+ ::CreateFileMappingW(OrigFileHandle, 0, flprotect,
+ Hi_32(Size),
+ Lo_32(Size),
+ 0);
+ if (FileMappingHandle == NULL) {
+ std::error_code ec = mapWindowsError(GetLastError());
+ return ec;
+ }
+
+ DWORD dwDesiredAccess;
+ switch (Mode) {
+ case readonly: dwDesiredAccess = FILE_MAP_READ; break;
+ case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break;
+ case priv: dwDesiredAccess = FILE_MAP_COPY; break;
+ }
+ Mapping = ::MapViewOfFile(FileMappingHandle,
+ dwDesiredAccess,
+ Offset >> 32,
+ Offset & 0xffffffff,
+ Size);
+ if (Mapping == NULL) {
+ std::error_code ec = mapWindowsError(GetLastError());
+ ::CloseHandle(FileMappingHandle);
+ return ec;
+ }
+
+ if (Size == 0) {
+ MEMORY_BASIC_INFORMATION mbi;
+ SIZE_T Result = VirtualQuery(Mapping, &mbi, sizeof(mbi));
+ if (Result == 0) {
+ std::error_code ec = mapWindowsError(GetLastError());
+ ::UnmapViewOfFile(Mapping);
+ ::CloseHandle(FileMappingHandle);
+ return ec;
+ }
+ Size = mbi.RegionSize;
+ }
+
+ // Close the file mapping handle, as it's kept alive by the file mapping. But
+ // neither the file mapping nor the file mapping handle keep the file handle
+ // alive, so we need to keep a reference to the file in case all other handles
+ // are closed and the file is deleted, which may cause invalid data to be read
+ // from the file.
+ ::CloseHandle(FileMappingHandle);
+ if (!::DuplicateHandle(::GetCurrentProcess(), OrigFileHandle,
+ ::GetCurrentProcess(), &FileHandle, 0, 0,
+ DUPLICATE_SAME_ACCESS)) {
+ std::error_code ec = mapWindowsError(GetLastError());
+ ::UnmapViewOfFile(Mapping);
+ return ec;
+ }
+
+ return std::error_code();
+}
+
+mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
+ uint64_t offset, std::error_code &ec)
+ : Size(length), Mapping() {
+ ec = init(fd, offset, mode);
+ if (ec)
+ Mapping = 0;
+}
+
+static bool hasFlushBufferKernelBug() {
+ static bool Ret{GetWindowsOSVersion() < wpi::VersionTuple(10, 0, 0, 17763)};
+ return Ret;
+}
+
+static bool isEXE(StringRef Magic) {
+ static const char PEMagic[] = {'P', 'E', '\0', '\0'};
+ if (Magic.startswith(StringRef("MZ")) && Magic.size() >= 0x3c + 4) {
+ uint32_t off = read32le(Magic.data() + 0x3c);
+ // PE/COFF file, either EXE or DLL.
+ if (Magic.substr(off).startswith(StringRef(PEMagic, sizeof(PEMagic))))
+ return true;
+ }
+ return false;
+}
+
+mapped_file_region::~mapped_file_region() {
+ if (Mapping) {
+
+ bool Exe = isEXE(StringRef((char *)Mapping, Size));
+
+ ::UnmapViewOfFile(Mapping);
+
+ if (Mode == mapmode::readwrite && Exe && hasFlushBufferKernelBug()) {
+ // There is a Windows kernel bug, the exact trigger conditions of which
+ // are not well understood. When triggered, dirty pages are not properly
+ // flushed and subsequent process's attempts to read a file can return
+ // invalid data. Calling FlushFileBuffers on the write handle is
+ // sufficient to ensure that this bug is not triggered.
+ // The bug only occurs when writing an executable and executing it right
+ // after, under high I/O pressure.
+ ::FlushFileBuffers(FileHandle);
+ }
+
+ ::CloseHandle(FileHandle);
+ }
+}
+
+size_t mapped_file_region::size() const {
+ assert(Mapping && "Mapping failed but used anyway!");
+ return Size;
+}
+
+char *mapped_file_region::data() const {
+ assert(Mapping && "Mapping failed but used anyway!");
+ return reinterpret_cast<char*>(Mapping);
+}
+
+const char *mapped_file_region::const_data() const {
+ assert(Mapping && "Mapping failed but used anyway!");
+ return reinterpret_cast<const char*>(Mapping);
+}
+
+int mapped_file_region::alignment() {
+ SYSTEM_INFO SysInfo;
+ ::GetSystemInfo(&SysInfo);
+ return SysInfo.dwAllocationGranularity;
+}
+
+static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) {
+ return basic_file_status(file_type_from_attrs(FindData->dwFileAttributes),
+ perms_from_attrs(FindData->dwFileAttributes),
+ FindData->ftLastAccessTime.dwHighDateTime,
+ FindData->ftLastAccessTime.dwLowDateTime,
+ FindData->ftLastWriteTime.dwHighDateTime,
+ FindData->ftLastWriteTime.dwLowDateTime,
+ FindData->nFileSizeHigh, FindData->nFileSizeLow);
+}
+
+std::error_code detail::directory_iterator_construct(detail::DirIterState &IT,
+ StringRef Path,
+ bool FollowSymlinks) {
+ SmallVector<wchar_t, 128> PathUTF16;
+
+ if (std::error_code EC = widenPath(Path, PathUTF16))
+ return EC;
+
+ // Convert path to the format that Windows is happy with.
+ if (PathUTF16.size() > 0 &&
+ !is_separator(PathUTF16[Path.size() - 1]) &&
+ PathUTF16[Path.size() - 1] != L':') {
+ PathUTF16.push_back(L'\\');
+ PathUTF16.push_back(L'*');
+ } else {
+ PathUTF16.push_back(L'*');
+ }
+
+ // Get the first directory entry.
+ WIN32_FIND_DATAW FirstFind;
+ ScopedFindHandle FindHandle(::FindFirstFileExW(
+ c_str(PathUTF16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch,
+ NULL, FIND_FIRST_EX_LARGE_FETCH));
+ if (!FindHandle)
+ return mapWindowsError(::GetLastError());
+
+ size_t FilenameLen = ::wcslen(FirstFind.cFileName);
+ while ((FilenameLen == 1 && FirstFind.cFileName[0] == L'.') ||
+ (FilenameLen == 2 && FirstFind.cFileName[0] == L'.' &&
+ FirstFind.cFileName[1] == L'.'))
+ if (!::FindNextFileW(FindHandle, &FirstFind)) {
+ DWORD LastError = ::GetLastError();
+ // Check for end.
+ if (LastError == ERROR_NO_MORE_FILES)
+ return detail::directory_iterator_destruct(IT);
+ return mapWindowsError(LastError);
+ } else
+ FilenameLen = ::wcslen(FirstFind.cFileName);
+
+ // Construct the current directory entry.
+ SmallString<128> DirectoryEntryNameUTF8;
+ if (std::error_code EC =
+ UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName),
+ DirectoryEntryNameUTF8))
+ return EC;
+
+ IT.IterationHandle = intptr_t(FindHandle.take());
+ SmallString<128> DirectoryEntryPath(Path);
+ path::append(DirectoryEntryPath, DirectoryEntryNameUTF8);
+ IT.CurrentEntry =
+ directory_entry(DirectoryEntryPath, FollowSymlinks,
+ file_type_from_attrs(FirstFind.dwFileAttributes),
+ status_from_find_data(&FirstFind));
+
+ return std::error_code();
+}
+
+std::error_code detail::directory_iterator_destruct(detail::DirIterState &IT) {
+ if (IT.IterationHandle != 0)
+ // Closes the handle if it's valid.
+ ScopedFindHandle close(HANDLE(IT.IterationHandle));
+ IT.IterationHandle = 0;
+ IT.CurrentEntry = directory_entry();
+ return std::error_code();
+}
+
+std::error_code detail::directory_iterator_increment(detail::DirIterState &IT) {
+ WIN32_FIND_DATAW FindData;
+ if (!::FindNextFileW(HANDLE(IT.IterationHandle), &FindData)) {
+ DWORD LastError = ::GetLastError();
+ // Check for end.
+ if (LastError == ERROR_NO_MORE_FILES)
+ return detail::directory_iterator_destruct(IT);
+ return mapWindowsError(LastError);
+ }
+
+ size_t FilenameLen = ::wcslen(FindData.cFileName);
+ if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') ||
+ (FilenameLen == 2 && FindData.cFileName[0] == L'.' &&
+ FindData.cFileName[1] == L'.'))
+ return directory_iterator_increment(IT);
+
+ SmallString<128> DirectoryEntryPathUTF8;
+ if (std::error_code EC =
+ UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName),
+ DirectoryEntryPathUTF8))
+ return EC;
+
+ IT.CurrentEntry.replace_filename(
+ Twine(DirectoryEntryPathUTF8),
+ file_type_from_attrs(FindData.dwFileAttributes),
+ status_from_find_data(&FindData));
+ return std::error_code();
+}
+
+ErrorOr<basic_file_status> directory_entry::status() const {
+ return Status;
+}
+
+static std::error_code nativeFileToFd(Expected<HANDLE> H, int &ResultFD,
+ OpenFlags Flags) {
+ int CrtOpenFlags = 0;
+ if (Flags & OF_Append)
+ CrtOpenFlags |= _O_APPEND;
+
+ if (Flags & OF_Text)
+ CrtOpenFlags |= _O_TEXT;
+
+ ResultFD = -1;
+ if (!H)
+ return errorToErrorCode(H.takeError());
+
+ ResultFD = ::_open_osfhandle(intptr_t(*H), CrtOpenFlags);
+ if (ResultFD == -1) {
+ ::CloseHandle(*H);
+ return mapWindowsError(ERROR_INVALID_HANDLE);
+ }
+ return std::error_code();
+}
+
+static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) {
+ // This is a compatibility hack. Really we should respect the creation
+ // disposition, but a lot of old code relied on the implicit assumption that
+ // OF_Append implied it would open an existing file. Since the disposition is
+ // now explicit and defaults to CD_CreateAlways, this assumption would cause
+ // any usage of OF_Append to append to a new file, even if the file already
+ // existed. A better solution might have two new creation dispositions:
+ // CD_AppendAlways and CD_AppendNew. This would also address the problem of
+ // OF_Append being used on a read-only descriptor, which doesn't make sense.
+ if (Flags & OF_Append)
+ return OPEN_ALWAYS;
+
+ switch (Disp) {
+ case CD_CreateAlways:
+ return CREATE_ALWAYS;
+ case CD_CreateNew:
+ return CREATE_NEW;
+ case CD_OpenAlways:
+ return OPEN_ALWAYS;
+ case CD_OpenExisting:
+ return OPEN_EXISTING;
+ }
+ wpi_unreachable("unreachable!");
+}
+
+static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) {
+ DWORD Result = 0;
+ if (Access & FA_Read)
+ Result |= GENERIC_READ;
+ if (Access & FA_Write)
+ Result |= GENERIC_WRITE;
+ if (Flags & OF_Delete)
+ Result |= DELETE;
+ if (Flags & OF_UpdateAtime)
+ Result |= FILE_WRITE_ATTRIBUTES;
+ return Result;
+}
+
+static std::error_code openNativeFileInternal(const Twine &Name,
+ file_t &ResultFile, DWORD Disp,
+ DWORD Access, DWORD Flags,
+ bool Inherit = false) {
+ SmallVector<wchar_t, 128> PathUTF16;
+ if (std::error_code EC = widenPath(Name, PathUTF16))
+ return EC;
+
+ SECURITY_ATTRIBUTES SA;
+ SA.nLength = sizeof(SA);
+ SA.lpSecurityDescriptor = nullptr;
+ SA.bInheritHandle = Inherit;
+
+ HANDLE H =
+ ::CreateFileW(PathUTF16.begin(), Access,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA,
+ Disp, Flags, NULL);
+ if (H == INVALID_HANDLE_VALUE) {
+ DWORD LastError = ::GetLastError();
+ std::error_code EC = mapWindowsError(LastError);
+ // Provide a better error message when trying to open directories.
+ // This only runs if we failed to open the file, so there is probably
+ // no performances issues.
+ if (LastError != ERROR_ACCESS_DENIED)
+ return EC;
+ if (is_directory(Name))
+ return make_error_code(errc::is_a_directory);
+ return EC;
+ }
+ ResultFile = H;
+ return std::error_code();
+}
+
+Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
+ FileAccess Access, OpenFlags Flags,
+ unsigned Mode) {
+ // Verify that we don't have both "append" and "excl".
+ assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
+ "Cannot specify both 'CreateNew' and 'Append' file creation flags!");
+
+ DWORD NativeDisp = nativeDisposition(Disp, Flags);
+ DWORD NativeAccess = nativeAccess(Access, Flags);
+
+ bool Inherit = false;
+ if (Flags & OF_ChildInherit)
+ Inherit = true;
+
+ file_t Result;
+ std::error_code EC = openNativeFileInternal(
+ Name, Result, NativeDisp, NativeAccess, FILE_ATTRIBUTE_NORMAL, Inherit);
+ if (EC)
+ return errorCodeToError(EC);
+
+ if (Flags & OF_UpdateAtime) {
+ FILETIME FileTime;
+ SYSTEMTIME SystemTime;
+ GetSystemTime(&SystemTime);
+ if (SystemTimeToFileTime(&SystemTime, &FileTime) == 0 ||
+ SetFileTime(Result, NULL, &FileTime, NULL) == 0) {
+ DWORD LastError = ::GetLastError();
+ ::CloseHandle(Result);
+ return errorCodeToError(mapWindowsError(LastError));
+ }
+ }
+
+ if (Flags & OF_Delete) {
+ if ((EC = setDeleteDisposition(Result, true))) {
+ ::CloseHandle(Result);
+ return errorCodeToError(EC);
+ }
+ }
+ return Result;
+}
+
+std::error_code openFile(const Twine &Name, int &ResultFD,
+ CreationDisposition Disp, FileAccess Access,
+ OpenFlags Flags, unsigned int Mode) {
+ Expected<file_t> Result = openNativeFile(Name, Disp, Access, Flags);
+ if (!Result)
+ return errorToErrorCode(Result.takeError());
+
+ return nativeFileToFd(*Result, ResultFD, Flags);
+}
+
+static std::error_code directoryRealPath(const Twine &Name,
+ SmallVectorImpl<char> &RealPath) {
+ file_t File;
+ std::error_code EC = openNativeFileInternal(
+ Name, File, OPEN_EXISTING, GENERIC_READ, FILE_FLAG_BACKUP_SEMANTICS);
+ if (EC)
+ return EC;
+
+ EC = realPathFromHandle(File, RealPath);
+ ::CloseHandle(File);
+ return EC;
+}
+
+std::error_code openFileForRead(const Twine &Name, int &ResultFD,
+ OpenFlags Flags,
+ SmallVectorImpl<char> *RealPath) {
+ Expected<HANDLE> NativeFile = openNativeFileForRead(Name, Flags, RealPath);
+ return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None);
+}
+
+Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
+ SmallVectorImpl<char> *RealPath) {
+ Expected<file_t> Result =
+ openNativeFile(Name, CD_OpenExisting, FA_Read, Flags);
+
+ // Fetch the real name of the file, if the user asked
+ if (Result && RealPath)
+ realPathFromHandle(*Result, *RealPath);
+
+ return Result;
+}
+
+void closeFile(file_t &F) {
+ ::CloseHandle(F);
+ F = kInvalidFile;
+}
+
+
+} // end namespace fs
+
+namespace path {
+static bool getKnownFolderPath(KNOWNFOLDERID folderId,
+ SmallVectorImpl<char> &result) {
+ wchar_t *path = nullptr;
+ if (::SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, nullptr, &path) != S_OK)
+ return false;
+
+ bool ok = !UTF16ToUTF8(path, ::wcslen(path), result);
+ ::CoTaskMemFree(path);
+ return ok;
+}
+
+bool home_directory(SmallVectorImpl<char> &result) {
+ return getKnownFolderPath(FOLDERID_Profile, result);
+}
+
+static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) {
+ SmallVector<wchar_t, 1024> Buf;
+ size_t Size = 1024;
+ do {
+ Buf.reserve(Size);
+ Size = GetEnvironmentVariableW(Var, Buf.data(), Buf.capacity());
+ if (Size == 0)
+ return false;
+
+ // Try again with larger buffer.
+ } while (Size > Buf.capacity());
+ Buf.set_size(Size);
+
+ return !windows::UTF16ToUTF8(Buf.data(), Size, Res);
+}
+
+static bool getTempDirEnvVar(SmallVectorImpl<char> &Res) {
+ const wchar_t *EnvironmentVariables[] = {L"TMP", L"TEMP", L"USERPROFILE"};
+ for (auto *Env : EnvironmentVariables) {
+ if (getTempDirEnvVar(Env, Res))
+ return true;
+ }
+ return false;
+}
+
+void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
+ (void)ErasedOnReboot;
+ Result.clear();
+
+ // Check whether the temporary directory is specified by an environment var.
+ // This matches GetTempPath logic to some degree. GetTempPath is not used
+ // directly as it cannot handle evn var longer than 130 chars on Windows 7
+ // (fixed on Windows 8).
+ if (getTempDirEnvVar(Result)) {
+ assert(!Result.empty() && "Unexpected empty path");
+ native(Result); // Some Unix-like shells use Unix path separator in $TMP.
+ fs::make_absolute(Result); // Make it absolute if not already.
+ return;
+ }
+
+ // Fall back to a system default.
+ const char *DefaultResult = "C:\\Temp";
+ Result.append(DefaultResult, DefaultResult + strlen(DefaultResult));
+}
+} // end namespace path
+
+namespace windows {
+std::error_code CodePageToUTF16(unsigned codepage,
+ wpi::StringRef original,
+ wpi::SmallVectorImpl<wchar_t> &utf16) {
+ if (!original.empty()) {
+ int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(),
+ original.size(), utf16.begin(), 0);
+
+ if (len == 0) {
+ return mapWindowsError(::GetLastError());
+ }
+
+ utf16.reserve(len + 1);
+ utf16.set_size(len);
+
+ len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(),
+ original.size(), utf16.begin(), utf16.size());
+
+ if (len == 0) {
+ return mapWindowsError(::GetLastError());
+ }
+ }
+
+ // Make utf16 null terminated.
+ utf16.push_back(0);
+ utf16.pop_back();
+
+ return std::error_code();
+}
+
+std::error_code UTF8ToUTF16(wpi::StringRef utf8,
+ wpi::SmallVectorImpl<wchar_t> &utf16) {
+ return CodePageToUTF16(CP_UTF8, utf8, utf16);
+}
+
+std::error_code CurCPToUTF16(wpi::StringRef curcp,
+ wpi::SmallVectorImpl<wchar_t> &utf16) {
+ return CodePageToUTF16(CP_ACP, curcp, utf16);
+}
+
+static
+std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16,
+ size_t utf16_len,
+ wpi::SmallVectorImpl<char> &converted) {
+ if (utf16_len) {
+ // Get length.
+ int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(),
+ 0, NULL, NULL);
+
+ if (len == 0) {
+ return mapWindowsError(::GetLastError());
+ }
+
+ converted.reserve(len);
+ converted.set_size(len);
+
+ // Now do the actual conversion.
+ len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(),
+ converted.size(), NULL, NULL);
+
+ if (len == 0) {
+ return mapWindowsError(::GetLastError());
+ }
+ }
+
+ // Make the new string null terminated.
+ converted.push_back(0);
+ converted.pop_back();
+
+ return std::error_code();
+}
+
+std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
+ wpi::SmallVectorImpl<char> &utf8) {
+ return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8);
+}
+
+std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
+ wpi::SmallVectorImpl<char> &curcp) {
+ return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp);
+}
+
+} // end namespace windows
+} // end namespace sys
+} // end namespace wpi
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
new file mode 100644
index 0000000..d830e33
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
@@ -0,0 +1,246 @@
+//===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines things specific to Windows implementations. In addition to
+// providing some helpers for working with win32 APIs, this header wraps
+// <windows.h> with some portability macros. Always include WindowsSupport.h
+// instead of including <windows.h> directly.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+//=== WARNING: Implementation here must contain only generic Win32 code that
+//=== is guaranteed to work on *all* Win32 variants.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H
+#define LLVM_SUPPORT_WINDOWSSUPPORT_H
+
+// mingw-w64 tends to define it as 0x0502 in its headers.
+#undef _WIN32_WINNT
+#undef _WIN32_IE
+
+// Require at least Windows 7 API.
+#define _WIN32_WINNT 0x0601
+#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
+#define WIN32_LEAN_AND_MEAN
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include "wpi/SmallVector.h"
+#include "wpi/StringExtras.h"
+#include "wpi/StringRef.h"
+#include "wpi/Twine.h"
+#include "wpi/Chrono.h"
+#include "wpi/Compiler.h"
+#include "wpi/VersionTuple.h"
+#include <cassert>
+#include <string>
+#include <system_error>
+#define WIN32_NO_STATUS
+#include <windows.h>
+#undef WIN32_NO_STATUS
+#include <winternl.h>
+#include <ntstatus.h>
+
+namespace wpi {
+
+/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
+/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
+/// GetVersionEx is deprecated, but this API exposes the build number which can
+/// be useful for working around certain kernel bugs.
+inline wpi::VersionTuple GetWindowsOSVersion() {
+ typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
+ HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
+ if (hMod) {
+ auto getVer = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
+ if (getVer) {
+ RTL_OSVERSIONINFOEXW info{};
+ info.dwOSVersionInfoSize = sizeof(info);
+ if (getVer((PRTL_OSVERSIONINFOW)&info) == ((NTSTATUS)0x00000000L)) {
+ return wpi::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
+ info.dwBuildNumber);
+ }
+ }
+ }
+ return wpi::VersionTuple(0, 0, 0, 0);
+}
+
+/// Determines if the program is running on Windows 8 or newer. This
+/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
+/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't
+/// yet have VersionHelpers.h, so we have our own helper.
+inline bool RunningWindows8OrGreater() {
+ // Windows 8 is version 6.2, service pack 0.
+ return GetWindowsOSVersion() >= wpi::VersionTuple(6, 2, 0, 0);
+}
+
+inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) {
+ if (!ErrMsg)
+ return true;
+ char *buffer = NULL;
+ DWORD LastError = GetLastError();
+ DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, LastError, 0, (LPSTR)&buffer, 1, NULL);
+ if (R)
+ *ErrMsg = prefix + ": " + buffer;
+ else
+ *ErrMsg = prefix + ": Unknown error";
+ *ErrMsg += " (0x" + wpi::utohexstr(LastError) + ")";
+
+ LocalFree(buffer);
+ return R != 0;
+}
+
+template <typename HandleTraits>
+class ScopedHandle {
+ typedef typename HandleTraits::handle_type handle_type;
+ handle_type Handle;
+
+ ScopedHandle(const ScopedHandle &other) = delete;
+ void operator=(const ScopedHandle &other) = delete;
+public:
+ ScopedHandle()
+ : Handle(HandleTraits::GetInvalid()) {}
+
+ explicit ScopedHandle(handle_type h)
+ : Handle(h) {}
+
+ ~ScopedHandle() {
+ if (HandleTraits::IsValid(Handle))
+ HandleTraits::Close(Handle);
+ }
+
+ handle_type take() {
+ handle_type t = Handle;
+ Handle = HandleTraits::GetInvalid();
+ return t;
+ }
+
+ ScopedHandle &operator=(handle_type h) {
+ if (HandleTraits::IsValid(Handle))
+ HandleTraits::Close(Handle);
+ Handle = h;
+ return *this;
+ }
+
+ // True if Handle is valid.
+ explicit operator bool() const {
+ return HandleTraits::IsValid(Handle) ? true : false;
+ }
+
+ operator handle_type() const {
+ return Handle;
+ }
+};
+
+struct CommonHandleTraits {
+ typedef HANDLE handle_type;
+
+ static handle_type GetInvalid() {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ static void Close(handle_type h) {
+ ::CloseHandle(h);
+ }
+
+ static bool IsValid(handle_type h) {
+ return h != GetInvalid();
+ }
+};
+
+struct JobHandleTraits : CommonHandleTraits {
+ static handle_type GetInvalid() {
+ return NULL;
+ }
+};
+
+struct RegTraits : CommonHandleTraits {
+ typedef HKEY handle_type;
+
+ static handle_type GetInvalid() {
+ return NULL;
+ }
+
+ static void Close(handle_type h) {
+ ::RegCloseKey(h);
+ }
+
+ static bool IsValid(handle_type h) {
+ return h != GetInvalid();
+ }
+};
+
+struct FindHandleTraits : CommonHandleTraits {
+ static void Close(handle_type h) {
+ ::FindClose(h);
+ }
+};
+
+struct FileHandleTraits : CommonHandleTraits {};
+
+typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
+typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
+typedef ScopedHandle<RegTraits> ScopedRegHandle;
+typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
+typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
+
+template <class T>
+class SmallVectorImpl;
+
+template <class T>
+typename SmallVectorImpl<T>::const_pointer
+c_str(SmallVectorImpl<T> &str) {
+ str.push_back(0);
+ str.pop_back();
+ return str.data();
+}
+
+namespace sys {
+
+inline std::chrono::nanoseconds toDuration(FILETIME Time) {
+ ULARGE_INTEGER TimeInteger;
+ TimeInteger.LowPart = Time.dwLowDateTime;
+ TimeInteger.HighPart = Time.dwHighDateTime;
+
+ // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
+ return std::chrono::nanoseconds(100 * TimeInteger.QuadPart);
+}
+
+inline TimePoint<> toTimePoint(FILETIME Time) {
+ ULARGE_INTEGER TimeInteger;
+ TimeInteger.LowPart = Time.dwLowDateTime;
+ TimeInteger.HighPart = Time.dwHighDateTime;
+
+ // Adjust for different epoch
+ TimeInteger.QuadPart -= 11644473600ll * 10000000;
+
+ // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
+ return TimePoint<>(std::chrono::nanoseconds(100 * TimeInteger.QuadPart));
+}
+
+inline FILETIME toFILETIME(TimePoint<> TP) {
+ ULARGE_INTEGER TimeInteger;
+ TimeInteger.QuadPart = TP.time_since_epoch().count() / 100;
+ TimeInteger.QuadPart += 11644473600ll * 10000000;
+
+ FILETIME Time;
+ Time.dwLowDateTime = TimeInteger.LowPart;
+ Time.dwHighDateTime = TimeInteger.HighPart;
+ return Time;
+}
+
+} // end namespace sys
+} // end namespace wpi.
+
+#endif
diff --git a/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp
new file mode 100644
index 0000000..1fb6c51
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp
@@ -0,0 +1,30 @@
+//===--- raw_os_ostream.cpp - Implement the raw_os_ostream class ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements support adapting raw_ostream to std::ostream.
+//
+//===----------------------------------------------------------------------===//
+
+#include "wpi/raw_os_ostream.h"
+#include <ostream>
+using namespace wpi;
+
+//===----------------------------------------------------------------------===//
+// raw_os_ostream
+//===----------------------------------------------------------------------===//
+
+raw_os_ostream::~raw_os_ostream() {
+ flush();
+}
+
+void raw_os_ostream::write_impl(const char *Ptr, size_t Size) {
+ OS.write(Ptr, Size);
+}
+
+uint64_t raw_os_ostream::current_pos() const { return OS.tellp(); }
diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
new file mode 100644
index 0000000..9f2942c
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
@@ -0,0 +1,903 @@
+//===--- raw_ostream.cpp - Implement the raw_ostream classes --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements support for bulk buffered stream output.
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef _WIN32
+#define _CRT_NONSTDC_NO_WARNINGS
+#endif
+
+#include "wpi/raw_ostream.h"
+#include "wpi/STLExtras.h"
+#include "wpi/SmallString.h"
+#include "wpi/SmallVector.h"
+#include "wpi/StringExtras.h"
+#include "wpi/Compiler.h"
+#include "wpi/ErrorHandling.h"
+#include "wpi/FileSystem.h"
+#include "wpi/Format.h"
+#include "wpi/MathExtras.h"
+#include "wpi/NativeFormatting.h"
+#include "wpi/WindowsError.h"
+#include <algorithm>
+#include <cctype>
+#include <cerrno>
+#include <cstdio>
+#include <iterator>
+#include <sys/stat.h>
+#include <system_error>
+
+// <fcntl.h> may provide O_BINARY.
+#include <fcntl.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/uio.h>
+#endif
+
+#if defined(__CYGWIN__)
+#include <io.h>
+#endif
+
+#if defined(_MSC_VER)
+#include <io.h>
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+# define STDERR_FILENO 2
+#endif
+#endif
+
+#ifdef _WIN32
+#include "wpi/ConvertUTF.h"
+#include "Windows/WindowsSupport.h"
+#endif
+
+using namespace wpi;
+
+raw_ostream::~raw_ostream() {
+ // raw_ostream's subclasses should take care to flush the buffer
+ // in their destructors.
+ assert(OutBufCur == OutBufStart &&
+ "raw_ostream destructor called with non-empty buffer!");
+
+ if (BufferMode == InternalBuffer)
+ delete [] OutBufStart;
+}
+
+// An out of line virtual method to provide a home for the class vtable.
+void raw_ostream::handle() {}
+
+size_t raw_ostream::preferred_buffer_size() const {
+ // BUFSIZ is intended to be a reasonable default.
+ return BUFSIZ;
+}
+
+void raw_ostream::SetBuffered() {
+ // Ask the subclass to determine an appropriate buffer size.
+ if (size_t Size = preferred_buffer_size())
+ SetBufferSize(Size);
+ else
+ // It may return 0, meaning this stream should be unbuffered.
+ SetUnbuffered();
+}
+
+void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
+ BufferKind Mode) {
+ assert(((Mode == Unbuffered && !BufferStart && Size == 0) ||
+ (Mode != Unbuffered && BufferStart && Size != 0)) &&
+ "stream must be unbuffered or have at least one byte");
+ // Make sure the current buffer is free of content (we can't flush here; the
+ // child buffer management logic will be in write_impl).
+ assert(GetNumBytesInBuffer() == 0 && "Current buffer is non-empty!");
+
+ if (BufferMode == InternalBuffer)
+ delete [] OutBufStart;
+ OutBufStart = BufferStart;
+ OutBufEnd = OutBufStart+Size;
+ OutBufCur = OutBufStart;
+ BufferMode = Mode;
+
+ assert(OutBufStart <= OutBufEnd && "Invalid size!");
+}
+
+raw_ostream &raw_ostream::operator<<(unsigned long N) {
+ write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
+ return *this;
+}
+
+raw_ostream &raw_ostream::operator<<(long N) {
+ write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
+ return *this;
+}
+
+raw_ostream &raw_ostream::operator<<(unsigned long long N) {
+ write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
+ return *this;
+}
+
+raw_ostream &raw_ostream::operator<<(long long N) {
+ write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
+ return *this;
+}
+
+raw_ostream &raw_ostream::write_hex(unsigned long long N) {
+ wpi::write_hex(*this, N, HexPrintStyle::Lower);
+ return *this;
+}
+
+raw_ostream &raw_ostream::write_escaped(StringRef Str,
+ bool UseHexEscapes) {
+ for (unsigned char c : Str) {
+ switch (c) {
+ case '\\':
+ *this << '\\' << '\\';
+ break;
+ case '\t':
+ *this << '\\' << 't';
+ break;
+ case '\n':
+ *this << '\\' << 'n';
+ break;
+ case '"':
+ *this << '\\' << '"';
+ break;
+ default:
+ if (isPrint(c)) {
+ *this << c;
+ break;
+ }
+
+ // Write out the escaped representation.
+ if (UseHexEscapes) {
+ *this << '\\' << 'x';
+ *this << hexdigit((c >> 4 & 0xF));
+ *this << hexdigit((c >> 0) & 0xF);
+ } else {
+ // Always use a full 3-character octal escape.
+ *this << '\\';
+ *this << char('0' + ((c >> 6) & 7));
+ *this << char('0' + ((c >> 3) & 7));
+ *this << char('0' + ((c >> 0) & 7));
+ }
+ }
+ }
+
+ return *this;
+}
+
+raw_ostream &raw_ostream::operator<<(const void *P) {
+ wpi::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower);
+ return *this;
+}
+
+raw_ostream &raw_ostream::operator<<(double N) {
+ wpi::write_double(*this, N, FloatStyle::Exponent);
+ return *this;
+}
+
+void raw_ostream::flush_nonempty() {
+ assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty.");
+ size_t Length = OutBufCur - OutBufStart;
+ OutBufCur = OutBufStart;
+ write_impl(OutBufStart, Length);
+}
+
+raw_ostream &raw_ostream::write(unsigned char C) {
+ // Group exceptional cases into a single branch.
+ if (LLVM_UNLIKELY(OutBufCur >= OutBufEnd)) {
+ if (LLVM_UNLIKELY(!OutBufStart)) {
+ if (BufferMode == Unbuffered) {
+ write_impl(reinterpret_cast<char*>(&C), 1);
+ return *this;
+ }
+ // Set up a buffer and start over.
+ SetBuffered();
+ return write(C);
+ }
+
+ flush_nonempty();
+ }
+
+ *OutBufCur++ = C;
+ return *this;
+}
+
+raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) {
+ // Group exceptional cases into a single branch.
+ if (LLVM_UNLIKELY(size_t(OutBufEnd - OutBufCur) < Size)) {
+ if (LLVM_UNLIKELY(!OutBufStart)) {
+ if (BufferMode == Unbuffered) {
+ write_impl(Ptr, Size);
+ return *this;
+ }
+ // Set up a buffer and start over.
+ SetBuffered();
+ return write(Ptr, Size);
+ }
+
+ size_t NumBytes = OutBufEnd - OutBufCur;
+
+ // If the buffer is empty at this point we have a string that is larger
+ // than the buffer. Directly write the chunk that is a multiple of the
+ // preferred buffer size and put the remainder in the buffer.
+ if (LLVM_UNLIKELY(OutBufCur == OutBufStart)) {
+ assert(NumBytes != 0 && "undefined behavior");
+ size_t BytesToWrite = Size - (Size % NumBytes);
+ write_impl(Ptr, BytesToWrite);
+ size_t BytesRemaining = Size - BytesToWrite;
+ if (BytesRemaining > size_t(OutBufEnd - OutBufCur)) {
+ // Too much left over to copy into our buffer.
+ return write(Ptr + BytesToWrite, BytesRemaining);
+ }
+ copy_to_buffer(Ptr + BytesToWrite, BytesRemaining);
+ return *this;
+ }
+
+ // We don't have enough space in the buffer to fit the string in. Insert as
+ // much as possible, flush and start over with the remainder.
+ copy_to_buffer(Ptr, NumBytes);
+ flush_nonempty();
+ return write(Ptr + NumBytes, Size - NumBytes);
+ }
+
+ copy_to_buffer(Ptr, Size);
+
+ return *this;
+}
+
+void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) {
+ assert(Size <= size_t(OutBufEnd - OutBufCur) && "Buffer overrun!");
+
+ // Handle short strings specially, memcpy isn't very good at very short
+ // strings.
+ switch (Size) {
+ case 4: OutBufCur[3] = Ptr[3]; LLVM_FALLTHROUGH;
+ case 3: OutBufCur[2] = Ptr[2]; LLVM_FALLTHROUGH;
+ case 2: OutBufCur[1] = Ptr[1]; LLVM_FALLTHROUGH;
+ case 1: OutBufCur[0] = Ptr[0]; LLVM_FALLTHROUGH;
+ case 0: break;
+ default:
+ memcpy(OutBufCur, Ptr, Size);
+ break;
+ }
+
+ OutBufCur += Size;
+}
+
+// Formatted output.
+raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
+ // If we have more than a few bytes left in our output buffer, try
+ // formatting directly onto its end.
+ size_t NextBufferSize = 127;
+ size_t BufferBytesLeft = OutBufEnd - OutBufCur;
+ if (BufferBytesLeft > 3) {
+ size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft);
+
+ // Common case is that we have plenty of space.
+ if (BytesUsed <= BufferBytesLeft) {
+ OutBufCur += BytesUsed;
+ return *this;
+ }
+
+ // Otherwise, we overflowed and the return value tells us the size to try
+ // again with.
+ NextBufferSize = BytesUsed;
+ }
+
+ // If we got here, we didn't have enough space in the output buffer for the
+ // string. Try printing into a SmallVector that is resized to have enough
+ // space. Iterate until we win.
+ SmallVector<char, 128> V;
+
+ while (true) {
+ V.resize(NextBufferSize);
+
+ // Try formatting into the SmallVector.
+ size_t BytesUsed = Fmt.print(V.data(), NextBufferSize);
+
+ // If BytesUsed fit into the vector, we win.
+ if (BytesUsed <= NextBufferSize)
+ return write(V.data(), BytesUsed);
+
+ // Otherwise, try again with a new size.
+ assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?");
+ NextBufferSize = BytesUsed;
+ }
+}
+
+raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
+ if (FS.Str.size() >= FS.Width || FS.Justify == FormattedString::JustifyNone) {
+ this->operator<<(FS.Str);
+ return *this;
+ }
+ const size_t Difference = FS.Width - FS.Str.size();
+ switch (FS.Justify) {
+ case FormattedString::JustifyLeft:
+ this->operator<<(FS.Str);
+ this->indent(Difference);
+ break;
+ case FormattedString::JustifyRight:
+ this->indent(Difference);
+ this->operator<<(FS.Str);
+ break;
+ case FormattedString::JustifyCenter: {
+ int PadAmount = Difference / 2;
+ this->indent(PadAmount);
+ this->operator<<(FS.Str);
+ this->indent(Difference - PadAmount);
+ break;
+ }
+ default:
+ wpi_unreachable("Bad Justification");
+ }
+ return *this;
+}
+
+raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
+ if (FN.Hex) {
+ HexPrintStyle Style;
+ if (FN.Upper && FN.HexPrefix)
+ Style = HexPrintStyle::PrefixUpper;
+ else if (FN.Upper && !FN.HexPrefix)
+ Style = HexPrintStyle::Upper;
+ else if (!FN.Upper && FN.HexPrefix)
+ Style = HexPrintStyle::PrefixLower;
+ else
+ Style = HexPrintStyle::Lower;
+ wpi::write_hex(*this, FN.HexValue, Style, FN.Width);
+ } else {
+ wpi::SmallString<16> Buffer;
+ wpi::raw_svector_ostream Stream(Buffer);
+ wpi::write_integer(Stream, FN.DecValue, 0, IntegerStyle::Integer);
+ if (Buffer.size() < FN.Width)
+ indent(FN.Width - Buffer.size());
+ (*this) << Buffer;
+ }
+ return *this;
+}
+
+raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) {
+ if (FB.Bytes.empty())
+ return *this;
+
+ size_t LineIndex = 0;
+ auto Bytes = FB.Bytes;
+ const size_t Size = Bytes.size();
+ HexPrintStyle HPS = FB.Upper ? HexPrintStyle::Upper : HexPrintStyle::Lower;
+ uint64_t OffsetWidth = 0;
+ if (FB.FirstByteOffset.has_value()) {
+ // Figure out how many nibbles are needed to print the largest offset
+ // represented by this data set, so that we can align the offset field
+ // to the right width.
+ size_t Lines = Size / FB.NumPerLine;
+ uint64_t MaxOffset = *FB.FirstByteOffset + Lines * FB.NumPerLine;
+ unsigned Power = 0;
+ if (MaxOffset > 0)
+ Power = wpi::Log2_64_Ceil(MaxOffset);
+ OffsetWidth = std::max<uint64_t>(4, wpi::alignTo(Power, 4) / 4);
+ }
+
+ // The width of a block of data including all spaces for group separators.
+ unsigned NumByteGroups =
+ alignTo(FB.NumPerLine, FB.ByteGroupSize) / FB.ByteGroupSize;
+ unsigned BlockCharWidth = FB.NumPerLine * 2 + NumByteGroups - 1;
+
+ while (!Bytes.empty()) {
+ indent(FB.IndentLevel);
+
+ if (FB.FirstByteOffset.has_value()) {
+ uint64_t Offset = FB.FirstByteOffset.value();
+ wpi::write_hex(*this, Offset + LineIndex, HPS, OffsetWidth);
+ *this << ": ";
+ }
+
+ auto Line = Bytes.take_front(FB.NumPerLine);
+
+ size_t CharsPrinted = 0;
+ // Print the hex bytes for this line in groups
+ for (size_t I = 0; I < Line.size(); ++I, CharsPrinted += 2) {
+ if (I && (I % FB.ByteGroupSize) == 0) {
+ ++CharsPrinted;
+ *this << " ";
+ }
+ wpi::write_hex(*this, Line[I], HPS, 2);
+ }
+
+ if (FB.ASCII) {
+ // Print any spaces needed for any bytes that we didn't print on this
+ // line so that the ASCII bytes are correctly aligned.
+ assert(BlockCharWidth >= CharsPrinted);
+ indent(BlockCharWidth - CharsPrinted + 2);
+ *this << "|";
+
+ // Print the ASCII char values for each byte on this line
+ for (uint8_t Byte : Line) {
+ if (isPrint(Byte))
+ *this << static_cast<char>(Byte);
+ else
+ *this << '.';
+ }
+ *this << '|';
+ }
+
+ Bytes = Bytes.drop_front(Line.size());
+ LineIndex += Line.size();
+ if (LineIndex < Size)
+ *this << '\n';
+ }
+ return *this;
+}
+
+template <char C>
+static raw_ostream &write_padding(raw_ostream &OS, unsigned NumChars) {
+ static const char Chars[] = {C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
+ C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
+ C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
+ C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
+ C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C};
+
+ // Usually the indentation is small, handle it with a fastpath.
+ if (NumChars < array_lengthof(Chars))
+ return OS.write(Chars, NumChars);
+
+ while (NumChars) {
+ unsigned NumToWrite = std::min(NumChars,
+ (unsigned)array_lengthof(Chars)-1);
+ OS.write(Chars, NumToWrite);
+ NumChars -= NumToWrite;
+ }
+ return OS;
+}
+
+/// indent - Insert 'NumSpaces' spaces.
+raw_ostream &raw_ostream::indent(unsigned NumSpaces) {
+ return write_padding<' '>(*this, NumSpaces);
+}
+
+/// write_zeros - Insert 'NumZeros' nulls.
+raw_ostream &raw_ostream::write_zeros(unsigned NumZeros) {
+ return write_padding<'\0'>(*this, NumZeros);
+}
+
+void raw_ostream::anchor() {}
+
+//===----------------------------------------------------------------------===//
+// Formatted Output
+//===----------------------------------------------------------------------===//
+
+// Out of line virtual method.
+void format_object_base::home() {
+}
+
+//===----------------------------------------------------------------------===//
+// raw_fd_ostream
+//===----------------------------------------------------------------------===//
+
+static int getFD(StringRef Filename, std::error_code &EC,
+ sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
+ sys::fs::OpenFlags Flags) {
+ assert((Access & sys::fs::FA_Write) &&
+ "Cannot make a raw_ostream from a read-only descriptor!");
+
+ // Handle "-" as stdout. Note that when we do this, we consider ourself
+ // the owner of stdout and may set the "binary" flag globally based on Flags.
+ if (Filename == "-") {
+ EC = std::error_code();
+ // If user requested binary then put stdout into binary mode if
+ // possible.
+ if (!(Flags & sys::fs::OF_Text)) {
+#if defined(_WIN32)
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
+ }
+ return STDOUT_FILENO;
+ }
+
+ int FD;
+ if (Access & sys::fs::FA_Read)
+ EC = sys::fs::openFileForReadWrite(Filename, FD, Disp, Flags);
+ else
+ EC = sys::fs::openFileForWrite(Filename, FD, Disp, Flags);
+ if (EC)
+ return -1;
+
+ return FD;
+}
+
+raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC)
+ : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write,
+ sys::fs::OF_None) {}
+
+raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
+ sys::fs::CreationDisposition Disp)
+ : raw_fd_ostream(Filename, EC, Disp, sys::fs::FA_Write, sys::fs::OF_None) {}
+
+raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
+ sys::fs::FileAccess Access)
+ : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, Access,
+ sys::fs::OF_None) {}
+
+raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
+ sys::fs::OpenFlags Flags)
+ : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write,
+ Flags) {}
+
+raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
+ sys::fs::CreationDisposition Disp,
+ sys::fs::FileAccess Access,
+ sys::fs::OpenFlags Flags)
+ : raw_fd_ostream(getFD(Filename, EC, Disp, Access, Flags), true) {}
+
+/// FD is the file descriptor that this writes to. If ShouldClose is true, this
+/// closes the file when the stream is destroyed.
+raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered)
+ : raw_pwrite_stream(unbuffered), FD(fd), ShouldClose(shouldClose) {
+ if (FD < 0 ) {
+ ShouldClose = false;
+ return;
+ }
+
+ // Do not attempt to close stdout or stderr. We used to try to maintain the
+ // property that tools that support writing file to stdout should not also
+ // write informational output to stdout, but in practice we were never able to
+ // maintain this invariant. Many features have been added to LLVM and clang
+ // (-fdump-record-layouts, optimization remarks, etc) that print to stdout, so
+ // users must simply be aware that mixed output and remarks is a possibility.
+ if (FD <= STDERR_FILENO)
+ ShouldClose = false;
+
+#ifdef _WIN32
+ // Check if this is a console device. This is not equivalent to isatty.
+ IsWindowsConsole =
+ ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;
+#endif
+
+ // Get the starting position.
+ off_t loc = ::lseek(FD, 0, SEEK_CUR);
+#ifdef _WIN32
+ // MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes.
+ SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast<HANDLE>(::_get_osfhandle(FD))) != FILE_TYPE_PIPE;
+#else
+ SupportsSeeking = loc != (off_t)-1;
+#endif
+ if (!SupportsSeeking)
+ pos = 0;
+ else
+ pos = static_cast<uint64_t>(loc);
+}
+
+raw_fd_ostream::~raw_fd_ostream() {
+ if (FD >= 0) {
+ flush();
+ if (ShouldClose && ::close(FD) < 0)
+ error_detected(std::error_code(errno, std::generic_category()));
+ }
+
+#ifdef __MINGW32__
+ // On mingw, global dtors should not call exit().
+ // report_fatal_error() invokes exit(). We know report_fatal_error()
+ // might not write messages to stderr when any errors were detected
+ // on FD == 2.
+ if (FD == 2) return;
+#endif
+
+ // If there are any pending errors, report them now. Clients wishing
+ // to avoid report_fatal_error calls should check for errors with
+ // has_error() and clear the error flag with clear_error() before
+ // destructing raw_ostream objects which may have errors.
+ if (has_error())
+ report_fatal_error("IO failure on output stream: " + error().message(),
+ /*GenCrashDiag=*/false);
+}
+
+#if defined(_WIN32)
+// The most reliable way to print unicode in a Windows console is with
+// WriteConsoleW. To use that, first transcode from UTF-8 to UTF-16. This
+// assumes that LLVM programs always print valid UTF-8 to the console. The data
+// might not be UTF-8 for two major reasons:
+// 1. The program is printing binary (-filetype=obj -o -), in which case it
+// would have been gibberish anyway.
+// 2. The program is printing text in a semi-ascii compatible codepage like
+// shift-jis or cp1252.
+//
+// Most LLVM programs don't produce non-ascii text unless they are quoting
+// user source input. A well-behaved LLVM program should either validate that
+// the input is UTF-8 or transcode from the local codepage to UTF-8 before
+// quoting it. If they don't, this may mess up the encoding, but this is still
+// probably the best compromise we can make.
+static bool write_console_impl(int FD, StringRef Data) {
+ SmallVector<wchar_t, 256> WideText;
+
+ // Fall back to ::write if it wasn't valid UTF-8.
+ if (auto EC = sys::windows::UTF8ToUTF16(Data, WideText))
+ return false;
+
+ // On Windows 7 and earlier, WriteConsoleW has a low maximum amount of data
+ // that can be written to the console at a time.
+ size_t MaxWriteSize = WideText.size();
+ if (!RunningWindows8OrGreater())
+ MaxWriteSize = 32767;
+
+ size_t WCharsWritten = 0;
+ do {
+ size_t WCharsToWrite =
+ std::min(MaxWriteSize, WideText.size() - WCharsWritten);
+ DWORD ActuallyWritten;
+ bool Success =
+ ::WriteConsoleW((HANDLE)::_get_osfhandle(FD), &WideText[WCharsWritten],
+ WCharsToWrite, &ActuallyWritten,
+ /*Reserved=*/nullptr);
+
+ // The most likely reason for WriteConsoleW to fail is that FD no longer
+ // points to a console. Fall back to ::write. If this isn't the first loop
+ // iteration, something is truly wrong.
+ if (!Success)
+ return false;
+
+ WCharsWritten += ActuallyWritten;
+ } while (WCharsWritten != WideText.size());
+ return true;
+}
+#endif
+
+void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
+ assert(FD >= 0 && "File already closed.");
+ pos += Size;
+
+#if defined(_WIN32)
+ // If this is a Windows console device, try re-encoding from UTF-8 to UTF-16
+ // and using WriteConsoleW. If that fails, fall back to plain write().
+ if (IsWindowsConsole)
+ if (write_console_impl(FD, StringRef(Ptr, Size)))
+ return;
+#endif
+
+ // The maximum write size is limited to INT32_MAX. A write
+ // greater than SSIZE_MAX is implementation-defined in POSIX,
+ // and Windows _write requires 32 bit input.
+ size_t MaxWriteSize = INT32_MAX;
+
+#if defined(__linux__)
+ // It is observed that Linux returns EINVAL for a very large write (>2G).
+ // Make it a reasonably small value.
+ MaxWriteSize = 1024 * 1024 * 1024;
+#endif
+
+ do {
+ size_t ChunkSize = std::min(Size, MaxWriteSize);
+#ifdef _WIN32
+ int ret = ::_write(FD, Ptr, ChunkSize);
+#else
+ ssize_t ret = ::write(FD, Ptr, ChunkSize);
+#endif
+
+ if (ret < 0) {
+ // If it's a recoverable error, swallow it and retry the write.
+ //
+ // Ideally we wouldn't ever see EAGAIN or EWOULDBLOCK here, since
+ // raw_ostream isn't designed to do non-blocking I/O. However, some
+ // programs, such as old versions of bjam, have mistakenly used
+ // O_NONBLOCK. For compatibility, emulate blocking semantics by
+ // spinning until the write succeeds. If you don't want spinning,
+ // don't use O_NONBLOCK file descriptors with raw_ostream.
+ if (errno == EINTR || errno == EAGAIN
+#ifdef EWOULDBLOCK
+ || errno == EWOULDBLOCK
+#endif
+ )
+ continue;
+
+ // Otherwise it's a non-recoverable error. Note it and quit.
+ error_detected(std::error_code(errno, std::generic_category()));
+ break;
+ }
+
+ // The write may have written some or all of the data. Update the
+ // size and buffer pointer to reflect the remainder that needs
+ // to be written. If there are no bytes left, we're done.
+ Ptr += ret;
+ Size -= ret;
+ } while (Size > 0);
+}
+
+void raw_fd_ostream::close() {
+ assert(ShouldClose);
+ ShouldClose = false;
+ flush();
+ if (::close(FD) < 0)
+ error_detected(std::error_code(errno, std::generic_category()));
+ FD = -1;
+}
+
+uint64_t raw_fd_ostream::seek(uint64_t off) {
+ assert(SupportsSeeking && "Stream does not support seeking!");
+ flush();
+#ifdef _WIN32
+ pos = ::_lseeki64(FD, off, SEEK_SET);
+#else
+ pos = ::lseek(FD, off, SEEK_SET);
+#endif
+ if (pos == (uint64_t)-1)
+ error_detected(std::error_code(errno, std::generic_category()));
+ return pos;
+}
+
+void raw_fd_ostream::pwrite_impl(const char *Ptr, size_t Size,
+ uint64_t Offset) {
+ uint64_t Pos = tell();
+ seek(Offset);
+ write(Ptr, Size);
+ seek(Pos);
+}
+
+size_t raw_fd_ostream::preferred_buffer_size() const {
+#if defined(_WIN32)
+ // Disable buffering for console devices. Console output is re-encoded from
+ // UTF-8 to UTF-16 on Windows, and buffering it would require us to split the
+ // buffer on a valid UTF-8 codepoint boundary. Terminal buffering is disabled
+ // below on most other OSs, so do the same thing on Windows and avoid that
+ // complexity.
+ if (IsWindowsConsole)
+ return 0;
+ return raw_ostream::preferred_buffer_size();
+#elif !defined(__minix)
+ // Minix has no st_blksize.
+ assert(FD >= 0 && "File not yet open!");
+ struct stat statbuf;
+ if (fstat(FD, &statbuf) != 0)
+ return 0;
+
+ // If this is a terminal, don't use buffering. Line buffering
+ // would be a more traditional thing to do, but it's not worth
+ // the complexity.
+ if (S_ISCHR(statbuf.st_mode) && isatty(FD))
+ return 0;
+ // Return the preferred block size.
+ return statbuf.st_blksize;
+#else
+ return raw_ostream::preferred_buffer_size();
+#endif
+}
+
+void raw_fd_ostream::anchor() {}
+
+//===----------------------------------------------------------------------===//
+// outs(), errs(), nulls()
+//===----------------------------------------------------------------------===//
+
+/// outs() - This returns a reference to a raw_ostream for standard output.
+/// Use it like: outs() << "foo" << "bar";
+raw_ostream &wpi::outs() {
+ // Set buffer settings to model stdout behavior.
+ std::error_code EC;
+ static raw_fd_ostream* S = new raw_fd_ostream("-", EC, sys::fs::F_None);
+ assert(!EC);
+ return *S;
+}
+
+/// errs() - This returns a reference to a raw_ostream for standard error.
+/// Use it like: errs() << "foo" << "bar";
+raw_ostream &wpi::errs() {
+ // Set standard error to be unbuffered by default.
+ static raw_fd_ostream* S = new raw_fd_ostream(STDERR_FILENO, false, true);
+ return *S;
+}
+
+/// nulls() - This returns a reference to a raw_ostream which discards output.
+raw_ostream &wpi::nulls() {
+ static raw_null_ostream* S = new raw_null_ostream;
+ return *S;
+}
+
+//===----------------------------------------------------------------------===//
+// raw_string_ostream
+//===----------------------------------------------------------------------===//
+
+raw_string_ostream::~raw_string_ostream() {
+ flush();
+}
+
+void raw_string_ostream::write_impl(const char *Ptr, size_t Size) {
+ OS.append(Ptr, Size);
+}
+
+//===----------------------------------------------------------------------===//
+// raw_svector_ostream
+//===----------------------------------------------------------------------===//
+
+uint64_t raw_svector_ostream::current_pos() const { return OS.size(); }
+
+void raw_svector_ostream::write_impl(const char *Ptr, size_t Size) {
+ OS.append(Ptr, Ptr + Size);
+}
+
+void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size,
+ uint64_t Offset) {
+ memcpy(OS.data() + Offset, Ptr, Size);
+}
+
+//===----------------------------------------------------------------------===//
+// raw_vector_ostream
+//===----------------------------------------------------------------------===//
+
+uint64_t raw_vector_ostream::current_pos() const { return OS.size(); }
+
+void raw_vector_ostream::write_impl(const char *Ptr, size_t Size) {
+ OS.insert(OS.end(), Ptr, Ptr + Size);
+}
+
+void raw_vector_ostream::pwrite_impl(const char *Ptr, size_t Size,
+ uint64_t Offset) {
+ memcpy(OS.data() + Offset, Ptr, Size);
+}
+
+//===----------------------------------------------------------------------===//
+// raw_usvector_ostream
+//===----------------------------------------------------------------------===//
+
+uint64_t raw_usvector_ostream::current_pos() const { return OS.size(); }
+
+void raw_usvector_ostream::write_impl(const char *Ptr, size_t Size) {
+ OS.append(reinterpret_cast<const uint8_t *>(Ptr),
+ reinterpret_cast<const uint8_t *>(Ptr) + Size);
+}
+
+void raw_usvector_ostream::pwrite_impl(const char *Ptr, size_t Size,
+ uint64_t Offset) {
+ memcpy(OS.data() + Offset, Ptr, Size);
+}
+
+//===----------------------------------------------------------------------===//
+// raw_uvector_ostream
+//===----------------------------------------------------------------------===//
+
+uint64_t raw_uvector_ostream::current_pos() const { return OS.size(); }
+
+void raw_uvector_ostream::write_impl(const char *Ptr, size_t Size) {
+ OS.insert(OS.end(), reinterpret_cast<const uint8_t *>(Ptr),
+ reinterpret_cast<const uint8_t *>(Ptr) + Size);
+}
+
+void raw_uvector_ostream::pwrite_impl(const char *Ptr, size_t Size,
+ uint64_t Offset) {
+ memcpy(OS.data() + Offset, Ptr, Size);
+}
+
+//===----------------------------------------------------------------------===//
+// raw_null_ostream
+//===----------------------------------------------------------------------===//
+
+raw_null_ostream::~raw_null_ostream() {
+#ifndef NDEBUG
+ // ~raw_ostream asserts that the buffer is empty. This isn't necessary
+ // with raw_null_ostream, but it's better to have raw_null_ostream follow
+ // the rules than to change the rules just for raw_null_ostream.
+ flush();
+#endif
+}
+
+void raw_null_ostream::write_impl(const char * /*Ptr*/, size_t /*Size*/) {}
+
+uint64_t raw_null_ostream::current_pos() const {
+ return 0;
+}
+
+void raw_null_ostream::pwrite_impl(const char * /*Ptr*/, size_t /*Size*/,
+ uint64_t /*Offset*/) {}
+
+void raw_pwrite_stream::anchor() {}
+
+void buffer_ostream::anchor() {}
diff --git a/wpiutil/src/main/native/cpp/raw_istream.cpp b/wpiutil/src/main/native/cpp/raw_istream.cpp
new file mode 100644
index 0000000..f63c217
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/raw_istream.cpp
@@ -0,0 +1,140 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#define _CRT_NONSTDC_NO_WARNINGS
+
+#include "wpi/raw_istream.h"
+
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <cstdlib>
+#include <cstring>
+
+#include "wpi/FileSystem.h"
+#include "wpi/SmallVector.h"
+#include "wpi/StringRef.h"
+
+#if defined(_MSC_VER)
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+#endif
+
+using namespace wpi;
+
+StringRef raw_istream::getline(SmallVectorImpl<char>& buf, int maxLen) {
+ buf.clear();
+ for (int i = 0; i < maxLen; ++i) {
+ char c;
+ read(c);
+ if (has_error()) return StringRef{buf.data(), buf.size()};
+ if (c == '\r') continue;
+ buf.push_back(c);
+ if (c == '\n') break;
+ }
+ return StringRef{buf.data(), buf.size()};
+}
+
+void raw_mem_istream::close() {}
+
+size_t raw_mem_istream::in_avail() const { return m_left; }
+
+void raw_mem_istream::read_impl(void* data, size_t len) {
+ if (len > m_left) {
+ error_detected();
+ len = m_left;
+ }
+ std::memcpy(data, m_cur, len);
+ m_cur += len;
+ m_left -= len;
+ set_read_count(len);
+}
+
+static int getFD(const Twine& Filename, std::error_code& EC) {
+ // Handle "-" as stdin. Note that when we do this, we consider ourself
+ // the owner of stdin. This means that we can do things like close the
+ // file descriptor when we're done and set the "binary" flag globally.
+ if (Filename.isSingleStringRef() && Filename.getSingleStringRef() == "-") {
+ EC = std::error_code();
+ return STDIN_FILENO;
+ }
+
+ int FD;
+
+ EC = sys::fs::openFileForRead(Filename, FD);
+ if (EC) return -1;
+
+ EC = std::error_code();
+ return FD;
+}
+
+raw_fd_istream::raw_fd_istream(const Twine& filename, std::error_code& ec,
+ size_t bufSize)
+ : raw_fd_istream(getFD(filename, ec), true, bufSize) {}
+
+raw_fd_istream::raw_fd_istream(int fd, bool shouldClose, size_t bufSize)
+ : m_bufSize(bufSize), m_fd(fd), m_shouldClose(shouldClose) {
+ m_cur = m_end = m_buf = static_cast<char*>(std::malloc(bufSize));
+}
+
+raw_fd_istream::~raw_fd_istream() {
+ if (m_shouldClose) close();
+ std::free(m_buf);
+}
+
+void raw_fd_istream::close() {
+ if (m_fd >= 0) {
+ ::close(m_fd);
+ m_fd = -1;
+ }
+}
+
+size_t raw_fd_istream::in_avail() const { return m_end - m_cur; }
+
+void raw_fd_istream::read_impl(void* data, size_t len) {
+ char* cdata = static_cast<char*>(data);
+ size_t pos = 0;
+ while (static_cast<size_t>(m_end - m_cur) < len) {
+ // not enough data
+ if (m_cur == m_end) {
+#ifdef _WIN32
+ int count = ::_read(m_fd, m_buf, m_bufSize);
+#else
+ ssize_t count = ::read(m_fd, m_buf, m_bufSize);
+#endif
+ if (count <= 0) {
+ error_detected();
+ set_read_count(pos);
+ return;
+ }
+ m_cur = m_buf;
+ m_end = m_buf + count;
+ continue;
+ }
+
+ size_t left = m_end - m_cur;
+ std::memcpy(&cdata[pos], m_cur, left);
+ m_cur += left;
+ pos += left;
+ len -= left;
+ }
+
+ std::memcpy(&cdata[pos], m_cur, len);
+ m_cur += len;
+ pos += len;
+ set_read_count(pos);
+}
diff --git a/wpiutil/src/main/native/cpp/raw_socket_istream.cpp b/wpiutil/src/main/native/cpp/raw_socket_istream.cpp
new file mode 100644
index 0000000..68c4dd6
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/raw_socket_istream.cpp
@@ -0,0 +1,32 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/raw_socket_istream.h"
+
+#include "wpi/NetworkStream.h"
+
+using namespace wpi;
+
+void raw_socket_istream::read_impl(void* data, size_t len) {
+ char* cdata = static_cast<char*>(data);
+ size_t pos = 0;
+
+ while (pos < len) {
+ NetworkStream::Error err;
+ size_t count = m_stream.receive(&cdata[pos], len - pos, &err, m_timeout);
+ if (count == 0) {
+ error_detected();
+ break;
+ }
+ pos += count;
+ }
+ set_read_count(pos);
+}
+
+void raw_socket_istream::close() { m_stream.close(); }
+
+size_t raw_socket_istream::in_avail() const { return 0; }
diff --git a/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp b/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp
new file mode 100644
index 0000000..fb8d94d
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/raw_socket_ostream.cpp
@@ -0,0 +1,39 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/raw_socket_ostream.h"
+
+#include "wpi/NetworkStream.h"
+
+using namespace wpi;
+
+raw_socket_ostream::~raw_socket_ostream() {
+ flush();
+ if (m_shouldClose) close();
+}
+
+void raw_socket_ostream::write_impl(const char* data, size_t len) {
+ size_t pos = 0;
+
+ while (pos < len) {
+ NetworkStream::Error err;
+ size_t count = m_stream.send(&data[pos], len - pos, &err);
+ if (count == 0) {
+ error_detected();
+ return;
+ }
+ pos += count;
+ }
+}
+
+uint64_t raw_socket_ostream::current_pos() const { return 0; }
+
+void raw_socket_ostream::close() {
+ if (!m_shouldClose) return;
+ flush();
+ m_stream.close();
+}
diff --git a/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp b/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp
new file mode 100644
index 0000000..8fc103d
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/raw_uv_ostream.cpp
@@ -0,0 +1,40 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/raw_uv_ostream.h"
+
+#include <cstring>
+
+using namespace wpi;
+
+void raw_uv_ostream::write_impl(const char* data, size_t len) {
+ while (len > 0) {
+ // allocate additional buffers as required
+ if (m_left == 0) {
+ m_bufs.emplace_back(m_alloc());
+ // we want bufs() to always be valid, so set len=0 and keep track of the
+ // amount of space remaining separately
+ m_left = m_bufs.back().len;
+ m_bufs.back().len = 0;
+ assert(m_left != 0);
+ }
+
+ size_t amt = (std::min)(m_left, len);
+ auto& buf = m_bufs.back();
+ std::memcpy(buf.base + buf.len, data, amt);
+ data += amt;
+ len -= amt;
+ buf.len += amt;
+ m_left -= amt;
+ }
+}
+
+uint64_t raw_uv_ostream::current_pos() const {
+ uint64_t size = 0;
+ for (auto&& buf : m_bufs) size += buf.len;
+ return size;
+}
diff --git a/wpiutil/src/main/native/cpp/sha1.cpp b/wpiutil/src/main/native/cpp/sha1.cpp
new file mode 100644
index 0000000..e1346f4
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/sha1.cpp
@@ -0,0 +1,316 @@
+/*
+ sha1.cpp - source code of
+
+ ============
+ SHA-1 in C++
+ ============
+
+ 100% Public Domain.
+
+ Original C Code
+ -- Steve Reid <steve@edmweb.com>
+ Small changes to fit into bglibs
+ -- Bruce Guenter <bruce@untroubled.org>
+ Translation to simpler C++ Code
+ -- Volker Grabsch <vog@notjusthosting.com>
+ Safety fixes
+ -- Eugene Hopkinson <slowriot at voxelstorm dot com>
+*/
+
+#include "wpi/sha1.h"
+
+#include "wpi/SmallVector.h"
+#include "wpi/StringExtras.h"
+#include "wpi/raw_istream.h"
+#include "wpi/raw_ostream.h"
+
+using namespace wpi;
+
+static const size_t BLOCK_INTS =
+ 16; /* number of 32bit integers per SHA1 block */
+static const size_t BLOCK_BYTES = BLOCK_INTS * 4;
+
+static void reset(uint32_t digest[], size_t& buf_size, uint64_t& transforms) {
+ /* SHA1 initialization constants */
+ digest[0] = 0x67452301;
+ digest[1] = 0xefcdab89;
+ digest[2] = 0x98badcfe;
+ digest[3] = 0x10325476;
+ digest[4] = 0xc3d2e1f0;
+
+ /* Reset counters */
+ buf_size = 0;
+ transforms = 0;
+}
+
+static uint32_t rol(const uint32_t value, const size_t bits) {
+ return (value << bits) | (value >> (32 - bits));
+}
+
+static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i) {
+ return rol(block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^
+ block[i],
+ 1);
+}
+
+/*
+ * (R0+R1), R2, R3, R4 are the different operations used in SHA1
+ */
+
+static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
+ const uint32_t x, const uint32_t y, uint32_t& z,
+ const size_t i) {
+ z += ((w & (x ^ y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
+ const uint32_t x, const uint32_t y, uint32_t& z,
+ const size_t i) {
+ block[i] = blk(block, i);
+ z += ((w & (x ^ y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
+ const uint32_t x, const uint32_t y, uint32_t& z,
+ const size_t i) {
+ block[i] = blk(block, i);
+ z += (w ^ x ^ y) + block[i] + 0x6ed9eba1 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
+ const uint32_t x, const uint32_t y, uint32_t& z,
+ const size_t i) {
+ block[i] = blk(block, i);
+ z += (((w | x) & y) | (w & x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
+ w = rol(w, 30);
+}
+
+static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
+ const uint32_t x, const uint32_t y, uint32_t& z,
+ const size_t i) {
+ block[i] = blk(block, i);
+ z += (w ^ x ^ y) + block[i] + 0xca62c1d6 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+
+static void do_transform(uint32_t digest[], uint32_t block[BLOCK_INTS],
+ uint64_t& transforms) {
+ /* Copy digest[] to working vars */
+ uint32_t a = digest[0];
+ uint32_t b = digest[1];
+ uint32_t c = digest[2];
+ uint32_t d = digest[3];
+ uint32_t e = digest[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(block, a, b, c, d, e, 0);
+ R0(block, e, a, b, c, d, 1);
+ R0(block, d, e, a, b, c, 2);
+ R0(block, c, d, e, a, b, 3);
+ R0(block, b, c, d, e, a, 4);
+ R0(block, a, b, c, d, e, 5);
+ R0(block, e, a, b, c, d, 6);
+ R0(block, d, e, a, b, c, 7);
+ R0(block, c, d, e, a, b, 8);
+ R0(block, b, c, d, e, a, 9);
+ R0(block, a, b, c, d, e, 10);
+ R0(block, e, a, b, c, d, 11);
+ R0(block, d, e, a, b, c, 12);
+ R0(block, c, d, e, a, b, 13);
+ R0(block, b, c, d, e, a, 14);
+ R0(block, a, b, c, d, e, 15);
+ R1(block, e, a, b, c, d, 0);
+ R1(block, d, e, a, b, c, 1);
+ R1(block, c, d, e, a, b, 2);
+ R1(block, b, c, d, e, a, 3);
+ R2(block, a, b, c, d, e, 4);
+ R2(block, e, a, b, c, d, 5);
+ R2(block, d, e, a, b, c, 6);
+ R2(block, c, d, e, a, b, 7);
+ R2(block, b, c, d, e, a, 8);
+ R2(block, a, b, c, d, e, 9);
+ R2(block, e, a, b, c, d, 10);
+ R2(block, d, e, a, b, c, 11);
+ R2(block, c, d, e, a, b, 12);
+ R2(block, b, c, d, e, a, 13);
+ R2(block, a, b, c, d, e, 14);
+ R2(block, e, a, b, c, d, 15);
+ R2(block, d, e, a, b, c, 0);
+ R2(block, c, d, e, a, b, 1);
+ R2(block, b, c, d, e, a, 2);
+ R2(block, a, b, c, d, e, 3);
+ R2(block, e, a, b, c, d, 4);
+ R2(block, d, e, a, b, c, 5);
+ R2(block, c, d, e, a, b, 6);
+ R2(block, b, c, d, e, a, 7);
+ R3(block, a, b, c, d, e, 8);
+ R3(block, e, a, b, c, d, 9);
+ R3(block, d, e, a, b, c, 10);
+ R3(block, c, d, e, a, b, 11);
+ R3(block, b, c, d, e, a, 12);
+ R3(block, a, b, c, d, e, 13);
+ R3(block, e, a, b, c, d, 14);
+ R3(block, d, e, a, b, c, 15);
+ R3(block, c, d, e, a, b, 0);
+ R3(block, b, c, d, e, a, 1);
+ R3(block, a, b, c, d, e, 2);
+ R3(block, e, a, b, c, d, 3);
+ R3(block, d, e, a, b, c, 4);
+ R3(block, c, d, e, a, b, 5);
+ R3(block, b, c, d, e, a, 6);
+ R3(block, a, b, c, d, e, 7);
+ R3(block, e, a, b, c, d, 8);
+ R3(block, d, e, a, b, c, 9);
+ R3(block, c, d, e, a, b, 10);
+ R3(block, b, c, d, e, a, 11);
+ R4(block, a, b, c, d, e, 12);
+ R4(block, e, a, b, c, d, 13);
+ R4(block, d, e, a, b, c, 14);
+ R4(block, c, d, e, a, b, 15);
+ R4(block, b, c, d, e, a, 0);
+ R4(block, a, b, c, d, e, 1);
+ R4(block, e, a, b, c, d, 2);
+ R4(block, d, e, a, b, c, 3);
+ R4(block, c, d, e, a, b, 4);
+ R4(block, b, c, d, e, a, 5);
+ R4(block, a, b, c, d, e, 6);
+ R4(block, e, a, b, c, d, 7);
+ R4(block, d, e, a, b, c, 8);
+ R4(block, c, d, e, a, b, 9);
+ R4(block, b, c, d, e, a, 10);
+ R4(block, a, b, c, d, e, 11);
+ R4(block, e, a, b, c, d, 12);
+ R4(block, d, e, a, b, c, 13);
+ R4(block, c, d, e, a, b, 14);
+ R4(block, b, c, d, e, a, 15);
+
+ /* Add the working vars back into digest[] */
+ digest[0] += a;
+ digest[1] += b;
+ digest[2] += c;
+ digest[3] += d;
+ digest[4] += e;
+
+ /* Count the number of transformations */
+ transforms++;
+}
+
+static void buffer_to_block(const unsigned char* buffer,
+ uint32_t block[BLOCK_INTS]) {
+ /* Convert the std::string (byte buffer) to a uint32_t array (MSB) */
+ for (size_t i = 0; i < BLOCK_INTS; i++) {
+ block[i] = (buffer[4 * i + 3] & 0xff) | (buffer[4 * i + 2] & 0xff) << 8 |
+ (buffer[4 * i + 1] & 0xff) << 16 |
+ (buffer[4 * i + 0] & 0xff) << 24;
+ }
+}
+
+SHA1::SHA1() { reset(digest, buf_size, transforms); }
+
+void SHA1::Update(StringRef s) {
+ raw_mem_istream is(makeArrayRef(s.data(), s.size()));
+ Update(is);
+}
+
+void SHA1::Update(raw_istream& is) {
+ while (true) {
+ buf_size += is.readsome(&buffer[buf_size], BLOCK_BYTES - buf_size);
+ if (buf_size != BLOCK_BYTES) {
+ return;
+ }
+ uint32_t block[BLOCK_INTS];
+ buffer_to_block(buffer, block);
+ do_transform(digest, block, transforms);
+ buf_size = 0;
+ }
+}
+
+/*
+ * Add padding and return the message digest.
+ */
+
+static void finalize(uint32_t digest[], unsigned char* buffer, size_t& buf_size,
+ uint64_t& transforms, raw_ostream& os, bool hex) {
+ /* Total number of hashed bits */
+ uint64_t total_bits = (transforms * BLOCK_BYTES + buf_size) * 8;
+
+ /* Padding */
+ buffer[buf_size++] = 0x80;
+ for (size_t i = buf_size; i < BLOCK_BYTES; ++i) {
+ buffer[i] = 0x00;
+ }
+
+ uint32_t block[BLOCK_INTS];
+ buffer_to_block(buffer, block);
+
+ if (buf_size > BLOCK_BYTES - 8) {
+ do_transform(digest, block, transforms);
+ for (size_t i = 0; i < BLOCK_INTS - 2; i++) {
+ block[i] = 0;
+ }
+ }
+
+ /* Append total_bits, split this uint64_t into two uint32_t */
+ block[BLOCK_INTS - 1] = total_bits;
+ block[BLOCK_INTS - 2] = (total_bits >> 32);
+ do_transform(digest, block, transforms);
+
+ /* Hex string */
+ static const char* const LUT = "0123456789abcdef";
+ for (size_t i = 0; i < 5; i++) {
+ uint32_t v = digest[i];
+ if (hex) {
+ os << LUT[(v >> 28) & 0xf] << LUT[(v >> 24) & 0xf] << LUT[(v >> 20) & 0xf]
+ << LUT[(v >> 16) & 0xf] << LUT[(v >> 12) & 0xf] << LUT[(v >> 8) & 0xf]
+ << LUT[(v >> 4) & 0xf] << LUT[(v >> 0) & 0xf];
+ } else {
+ os.write(static_cast<unsigned char>((v >> 24) & 0xff));
+ os.write(static_cast<unsigned char>((v >> 16) & 0xff));
+ os.write(static_cast<unsigned char>((v >> 8) & 0xff));
+ os.write(static_cast<unsigned char>((v >> 0) & 0xff));
+ }
+ }
+
+ /* Reset for next run */
+ reset(digest, buf_size, transforms);
+}
+
+std::string SHA1::Final() {
+ std::string out;
+ raw_string_ostream os(out);
+
+ finalize(digest, buffer, buf_size, transforms, os, true);
+
+ return os.str();
+}
+
+StringRef SHA1::Final(SmallVectorImpl<char>& buf) {
+ raw_svector_ostream os(buf);
+
+ finalize(digest, buffer, buf_size, transforms, os, true);
+
+ return os.str();
+}
+
+StringRef SHA1::RawFinal(SmallVectorImpl<char>& buf) {
+ raw_svector_ostream os(buf);
+
+ finalize(digest, buffer, buf_size, transforms, os, false);
+
+ return os.str();
+}
+
+std::string SHA1::FromFile(StringRef filename) {
+ std::error_code ec;
+ raw_fd_istream stream(filename, ec);
+ SHA1 checksum;
+ checksum.Update(stream);
+ return checksum.Final();
+}
diff --git a/wpiutil/src/main/native/cpp/timestamp.cpp b/wpiutil/src/main/native/cpp/timestamp.cpp
new file mode 100644
index 0000000..7f3b0cf
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/timestamp.cpp
@@ -0,0 +1,108 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/timestamp.h"
+
+#include <atomic>
+
+#ifdef _WIN32
+#include <windows.h>
+
+#include <cassert>
+#include <exception>
+#else
+#include <chrono>
+#endif
+
+// offset in microseconds
+static uint64_t zerotime() {
+#ifdef _WIN32
+ FILETIME ft;
+ uint64_t tmpres = 0;
+ // 100-nanosecond intervals since January 1, 1601 (UTC)
+ // which means 0.1 us
+ GetSystemTimeAsFileTime(&ft);
+ tmpres |= ft.dwHighDateTime;
+ tmpres <<= 32;
+ tmpres |= ft.dwLowDateTime;
+ tmpres /= 10u; // convert to us
+ // January 1st, 1970 - January 1st, 1601 UTC ~ 369 years
+ // or 11644473600000000 us
+ static const uint64_t deltaepoch = 11644473600000000ull;
+ tmpres -= deltaepoch;
+ return tmpres;
+#else
+ // 1-us intervals
+ return std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::high_resolution_clock::now().time_since_epoch())
+ .count();
+#endif
+}
+
+static uint64_t timestamp() {
+#ifdef _WIN32
+ LARGE_INTEGER li;
+ QueryPerformanceCounter(&li);
+ // there is an imprecision with the initial value,
+ // but what matters is that timestamps are monotonic and consistent
+ return static_cast<uint64_t>(li.QuadPart);
+#else
+ // 1-us intervals
+ return std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::steady_clock::now().time_since_epoch())
+ .count();
+#endif
+}
+
+#ifdef _WIN32
+static uint64_t update_frequency() {
+ LARGE_INTEGER li;
+ if (!QueryPerformanceFrequency(&li) || !li.QuadPart) {
+ // log something
+ std::terminate();
+ }
+ return static_cast<uint64_t>(li.QuadPart);
+}
+#endif
+
+static const uint64_t zerotime_val = zerotime();
+static const uint64_t offset_val = timestamp();
+#ifdef _WIN32
+static const uint64_t frequency_val = update_frequency();
+#endif
+
+uint64_t wpi::NowDefault() {
+#ifdef _WIN32
+ assert(offset_val > 0u);
+ assert(frequency_val > 0u);
+ uint64_t delta = timestamp() - offset_val;
+ // because the frequency is in update per seconds, we have to multiply the
+ // delta by 1,000,000
+ uint64_t delta_in_us = delta * 1000000ull / frequency_val;
+ return delta_in_us + zerotime_val;
+#else
+ return zerotime_val + timestamp() - offset_val;
+#endif
+}
+
+static std::atomic<uint64_t (*)()> now_impl{wpi::NowDefault};
+
+void wpi::SetNowImpl(uint64_t (*func)(void)) {
+ now_impl = func ? func : NowDefault;
+}
+
+uint64_t wpi::Now() { return (now_impl.load())(); }
+
+extern "C" {
+
+uint64_t WPI_NowDefault(void) { return wpi::NowDefault(); }
+
+void WPI_SetNowImpl(uint64_t (*func)(void)) { wpi::SetNowImpl(func); }
+
+uint64_t WPI_Now(void) { return wpi::Now(); }
+
+} // extern "C"
diff --git a/wpiutil/src/main/native/cpp/uv/Async.cpp b/wpiutil/src/main/native/cpp/uv/Async.cpp
new file mode 100644
index 0000000..5479f49
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Async.cpp
@@ -0,0 +1,37 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Async.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+Async<>::~Async() noexcept {
+ if (auto loop = m_loop.lock())
+ Close();
+ else
+ ForceClosed();
+}
+
+std::shared_ptr<Async<>> Async<>::Create(const std::shared_ptr<Loop>& loop) {
+ auto h = std::make_shared<Async>(loop, private_init{});
+ int err = uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
+ Async& h = *static_cast<Async*>(handle->data);
+ h.wakeup();
+ });
+ if (err < 0) {
+ loop->ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Check.cpp b/wpiutil/src/main/native/cpp/uv/Check.cpp
new file mode 100644
index 0000000..0f4cccf
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Check.cpp
@@ -0,0 +1,34 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Check.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Check> Check::Create(Loop& loop) {
+ auto h = std::make_shared<Check>(private_init{});
+ int err = uv_check_init(loop.GetRaw(), h->GetRaw());
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Check::Start() {
+ Invoke(&uv_check_start, GetRaw(), [](uv_check_t* handle) {
+ Check& h = *static_cast<Check*>(handle->data);
+ h.check();
+ });
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/FsEvent.cpp b/wpiutil/src/main/native/cpp/uv/FsEvent.cpp
new file mode 100644
index 0000000..54ba31f
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/FsEvent.cpp
@@ -0,0 +1,67 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/FsEvent.h"
+
+#include <cstdlib>
+
+#include "wpi/SmallString.h"
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<FsEvent> FsEvent::Create(Loop& loop) {
+ auto h = std::make_shared<FsEvent>(private_init{});
+ int err = uv_fs_event_init(loop.GetRaw(), h->GetRaw());
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void FsEvent::Start(const Twine& path, unsigned int flags) {
+ SmallString<128> pathBuf;
+ Invoke(
+ &uv_fs_event_start, GetRaw(),
+ [](uv_fs_event_t* handle, const char* filename, int events, int status) {
+ FsEvent& h = *static_cast<FsEvent*>(handle->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.fsEvent(filename, events);
+ },
+ path.toNullTerminatedStringRef(pathBuf).data(), flags);
+}
+
+std::string FsEvent::GetPath() {
+ // Per the libuv docs, GetPath() always gives us a null-terminated string.
+ // common case should be small
+ char buf[128];
+ size_t size = 128;
+ int r = uv_fs_event_getpath(GetRaw(), buf, &size);
+ if (r == 0) {
+ return buf;
+ } else if (r == UV_ENOBUFS) {
+ // need to allocate a big enough buffer
+ char* buf2 = static_cast<char*>(std::malloc(size));
+ r = uv_fs_event_getpath(GetRaw(), buf2, &size);
+ if (r == 0) {
+ std::string out{buf2};
+ std::free(buf2);
+ return out;
+ }
+ std::free(buf2);
+ }
+ ReportError(r);
+ return std::string{};
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp b/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp
new file mode 100644
index 0000000..21f6404
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/GetAddrInfo.cpp
@@ -0,0 +1,55 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/GetAddrInfo.h"
+
+#include "wpi/uv/Loop.h"
+#include "wpi/uv/util.h"
+
+namespace wpi {
+namespace uv {
+
+GetAddrInfoReq::GetAddrInfoReq() {
+ error = [this](Error err) { GetLoop().error(err); };
+}
+
+void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
+ const Twine& node, const Twine& service,
+ const addrinfo* hints) {
+ SmallVector<char, 128> nodeStr;
+ SmallVector<char, 128> serviceStr;
+ int err = uv_getaddrinfo(
+ loop.GetRaw(), req->GetRaw(),
+ [](uv_getaddrinfo_t* req, int status, addrinfo* res) {
+ auto& h = *static_cast<GetAddrInfoReq*>(req->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.resolved(*res);
+ uv_freeaddrinfo(res);
+ h.Release(); // this is always a one-shot
+ },
+ node.isNull() ? nullptr : node.toNullTerminatedStringRef(nodeStr).data(),
+ service.isNull() ? nullptr
+ : service.toNullTerminatedStringRef(serviceStr).data(),
+ hints);
+ if (err < 0)
+ loop.ReportError(err);
+ else
+ req->Keep();
+}
+
+void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
+ const Twine& node, const Twine& service,
+ const addrinfo* hints) {
+ auto req = std::make_shared<GetAddrInfoReq>();
+ req->resolved.connect(callback);
+ GetAddrInfo(loop, req, node, service, hints);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp b/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp
new file mode 100644
index 0000000..994aadc
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/GetNameInfo.cpp
@@ -0,0 +1,90 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/GetNameInfo.h"
+
+#include "wpi/uv/Loop.h"
+#include "wpi/uv/util.h"
+
+namespace wpi {
+namespace uv {
+
+GetNameInfoReq::GetNameInfoReq() {
+ error = [this](Error err) { GetLoop().error(err); };
+}
+
+void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+ const sockaddr& addr, int flags) {
+ int err = uv_getnameinfo(loop.GetRaw(), req->GetRaw(),
+ [](uv_getnameinfo_t* req, int status,
+ const char* hostname, const char* service) {
+ auto& h = *static_cast<GetNameInfoReq*>(req->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.resolved(hostname, service);
+ h.Release(); // this is always a one-shot
+ },
+ &addr, flags);
+ if (err < 0)
+ loop.ReportError(err);
+ else
+ req->Keep();
+}
+
+void GetNameInfo(Loop& loop,
+ std::function<void(const char*, const char*)> callback,
+ const sockaddr& addr, int flags) {
+ auto req = std::make_shared<GetNameInfoReq>();
+ req->resolved.connect(callback);
+ GetNameInfo(loop, req, addr, flags);
+}
+
+void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+ const Twine& ip, unsigned int port, int flags) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ loop.ReportError(err);
+ else
+ GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+void GetNameInfo4(Loop& loop,
+ std::function<void(const char*, const char*)> callback,
+ const Twine& ip, unsigned int port, int flags) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ loop.ReportError(err);
+ else
+ GetNameInfo(loop, callback, reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
+ const Twine& ip, unsigned int port, int flags) {
+ sockaddr_in6 addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ loop.ReportError(err);
+ else
+ GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+void GetNameInfo6(Loop& loop,
+ std::function<void(const char*, const char*)> callback,
+ const Twine& ip, unsigned int port, int flags) {
+ sockaddr_in6 addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ loop.ReportError(err);
+ else
+ GetNameInfo(loop, callback, reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Handle.cpp b/wpiutil/src/main/native/cpp/uv/Handle.cpp
new file mode 100644
index 0000000..93ef423
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Handle.cpp
@@ -0,0 +1,36 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Handle.h"
+
+using namespace wpi::uv;
+
+Handle::~Handle() noexcept {
+ if (!m_closed && m_uv_handle->type != UV_UNKNOWN_HANDLE) {
+ uv_close(m_uv_handle, [](uv_handle_t* uv_handle) { delete uv_handle; });
+ } else {
+ delete m_uv_handle;
+ }
+}
+
+void Handle::Close() noexcept {
+ if (!IsClosing()) {
+ uv_close(m_uv_handle, [](uv_handle_t* handle) {
+ Handle& h = *static_cast<Handle*>(handle->data);
+ h.closed();
+ h.Release(); // free ourselves
+ });
+ m_closed = true;
+ }
+}
+
+void Handle::AllocBuf(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
+ auto& h = *static_cast<Handle*>(handle->data);
+ *buf = h.m_allocBuf(size);
+}
+
+void Handle::DefaultFreeBuf(Buffer& buf) { buf.Deallocate(); }
diff --git a/wpiutil/src/main/native/cpp/uv/Idle.cpp b/wpiutil/src/main/native/cpp/uv/Idle.cpp
new file mode 100644
index 0000000..9eae218
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Idle.cpp
@@ -0,0 +1,34 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Idle.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Idle> Idle::Create(Loop& loop) {
+ auto h = std::make_shared<Idle>(private_init{});
+ int err = uv_idle_init(loop.GetRaw(), h->GetRaw());
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Idle::Start() {
+ Invoke(&uv_idle_start, GetRaw(), [](uv_idle_t* handle) {
+ Idle& h = *static_cast<Idle*>(handle->data);
+ h.idle();
+ });
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Loop.cpp b/wpiutil/src/main/native/cpp/uv/Loop.cpp
new file mode 100644
index 0000000..2602150
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Loop.cpp
@@ -0,0 +1,64 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Loop.h"
+
+using namespace wpi::uv;
+
+Loop::Loop(const private_init&) noexcept {
+#ifndef _WIN32
+ // Ignore SIGPIPE (see https://github.com/joyent/libuv/issues/1254)
+ static bool once = []() {
+ signal(SIGPIPE, SIG_IGN);
+ return true;
+ }();
+ (void)once;
+#endif
+}
+
+Loop::~Loop() noexcept {
+ if (m_loop) {
+ m_loop->data = nullptr;
+ Close();
+ }
+}
+
+std::shared_ptr<Loop> Loop::Create() {
+ auto loop = std::make_shared<Loop>(private_init{});
+ if (uv_loop_init(&loop->m_loopStruct) < 0) return nullptr;
+ loop->m_loop = &loop->m_loopStruct;
+ loop->m_loop->data = loop.get();
+ return loop;
+}
+
+std::shared_ptr<Loop> Loop::GetDefault() {
+ static std::shared_ptr<Loop> loop = std::make_shared<Loop>(private_init{});
+ loop->m_loop = uv_default_loop();
+ if (!loop->m_loop) return nullptr;
+ loop->m_loop->data = loop.get();
+ return loop;
+}
+
+void Loop::Close() {
+ int err = uv_loop_close(m_loop);
+ if (err < 0) ReportError(err);
+}
+
+void Loop::Walk(std::function<void(Handle&)> callback) {
+ uv_walk(m_loop,
+ [](uv_handle_t* handle, void* func) {
+ auto& h = *static_cast<Handle*>(handle->data);
+ auto& f = *static_cast<std::function<void(Handle&)>*>(func);
+ f(h);
+ },
+ &callback);
+}
+
+void Loop::Fork() {
+ int err = uv_loop_fork(m_loop);
+ if (err < 0) ReportError(err);
+}
diff --git a/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp b/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp
new file mode 100644
index 0000000..b407c2b
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/NameToAddr.cpp
@@ -0,0 +1,68 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/util.h" // NOLINT(build/include_order)
+
+#include <cstring>
+
+#include "wpi/SmallString.h"
+
+namespace wpi {
+namespace uv {
+
+int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in* addr) {
+ SmallString<128> tmp;
+ StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
+ if (ipStr.empty()) {
+ std::memset(addr, 0, sizeof(sockaddr_in));
+ addr->sin_family = PF_INET;
+ addr->sin_addr.s_addr = INADDR_ANY;
+ addr->sin_port = htons(port);
+ return 0;
+ } else {
+ return uv_ip4_addr(ipStr.data(), port, addr);
+ }
+}
+
+int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in6* addr) {
+ SmallString<128> tmp;
+ StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
+ if (ipStr.empty()) {
+ std::memset(addr, 0, sizeof(sockaddr_in6));
+ addr->sin6_family = PF_INET6;
+ addr->sin6_addr = in6addr_any;
+ addr->sin6_port = htons(port);
+ return 0;
+ } else {
+ return uv_ip6_addr(ipStr.data(), port, addr);
+ }
+}
+
+int NameToAddr(const Twine& ip, in_addr* addr) {
+ SmallString<128> tmp;
+ StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
+ if (ipStr.empty()) {
+ addr->s_addr = INADDR_ANY;
+ return 0;
+ } else {
+ return uv_inet_pton(AF_INET, ipStr.data(), addr);
+ }
+}
+
+int NameToAddr(const Twine& ip, in6_addr* addr) {
+ SmallString<128> tmp;
+ StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
+ if (ipStr.empty()) {
+ *addr = in6addr_any;
+ return 0;
+ } else {
+ return uv_inet_pton(AF_INET6, ipStr.data(), addr);
+ }
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp b/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp
new file mode 100644
index 0000000..6e327a7
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/NetworkStream.cpp
@@ -0,0 +1,34 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/NetworkStream.h"
+
+namespace wpi {
+namespace uv {
+
+ConnectReq::ConnectReq() {
+ error = [this](Error err) { GetStream().error(err); };
+}
+
+void NetworkStream::Listen(int backlog) {
+ Invoke(&uv_listen, GetRawStream(), backlog,
+ [](uv_stream_t* handle, int status) {
+ auto& h = *static_cast<NetworkStream*>(handle->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.connection();
+ });
+}
+
+void NetworkStream::Listen(std::function<void()> callback, int backlog) {
+ connection.connect(callback);
+ Listen(backlog);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Pipe.cpp b/wpiutil/src/main/native/cpp/uv/Pipe.cpp
new file mode 100644
index 0000000..9db879a
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Pipe.cpp
@@ -0,0 +1,134 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Pipe.h"
+
+#include <cstdlib>
+
+#include "wpi/SmallString.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
+ auto h = std::make_shared<Pipe>(private_init{});
+ int err = uv_pipe_init(loop.GetRaw(), h->GetRaw(), ipc ? 1 : 0);
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Pipe::Reuse(std::function<void()> callback, bool ipc) {
+ if (IsClosing()) return;
+ if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
+ m_reuseData->callback = callback;
+ m_reuseData->ipc = ipc;
+ uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+ Pipe& h = *static_cast<Pipe*>(handle->data);
+ if (!h.m_reuseData) return;
+ auto data = std::move(h.m_reuseData);
+ auto err =
+ uv_pipe_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->ipc ? 1 : 0);
+ if (err < 0) {
+ h.ReportError(err);
+ return;
+ }
+ data->callback();
+ });
+}
+
+std::shared_ptr<Pipe> Pipe::Accept() {
+ auto client = Create(GetLoopRef(), GetRaw()->ipc);
+ if (!client) return nullptr;
+ if (!Accept(client)) {
+ client->Release();
+ return nullptr;
+ }
+ return client;
+}
+
+Pipe* Pipe::DoAccept() { return Accept().get(); }
+
+void Pipe::Bind(const Twine& name) {
+ SmallString<128> nameBuf;
+ Invoke(&uv_pipe_bind, GetRaw(),
+ name.toNullTerminatedStringRef(nameBuf).data());
+}
+
+void Pipe::Connect(const Twine& name,
+ const std::shared_ptr<PipeConnectReq>& req) {
+ SmallString<128> nameBuf;
+ uv_pipe_connect(req->GetRaw(), GetRaw(),
+ name.toNullTerminatedStringRef(nameBuf).data(),
+ [](uv_connect_t* req, int status) {
+ auto& h = *static_cast<PipeConnectReq*>(req->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.connected();
+ h.Release(); // this is always a one-shot
+ });
+ req->Keep();
+}
+
+void Pipe::Connect(const Twine& name, std::function<void()> callback) {
+ auto req = std::make_shared<PipeConnectReq>();
+ req->connected.connect(callback);
+ Connect(name, req);
+}
+
+std::string Pipe::GetSock() {
+ // Per libuv docs, the returned buffer is NOT null terminated.
+ // common case should be small
+ char buf[128];
+ size_t size = 128;
+ int r = uv_pipe_getsockname(GetRaw(), buf, &size);
+ if (r == 0) {
+ return std::string{buf, size};
+ } else if (r == UV_ENOBUFS) {
+ // need to allocate a big enough buffer
+ char* buf2 = static_cast<char*>(std::malloc(size));
+ r = uv_pipe_getsockname(GetRaw(), buf2, &size);
+ if (r == 0) {
+ std::string out{buf2, size};
+ std::free(buf2);
+ return out;
+ }
+ std::free(buf2);
+ }
+ ReportError(r);
+ return std::string{};
+}
+
+std::string Pipe::GetPeer() {
+ // Per libuv docs, the returned buffer is NOT null terminated.
+ // common case should be small
+ char buf[128];
+ size_t size = 128;
+ int r = uv_pipe_getpeername(GetRaw(), buf, &size);
+ if (r == 0) {
+ return std::string{buf, size};
+ } else if (r == UV_ENOBUFS) {
+ // need to allocate a big enough buffer
+ char* buf2 = static_cast<char*>(std::malloc(size));
+ r = uv_pipe_getpeername(GetRaw(), buf2, &size);
+ if (r == 0) {
+ std::string out{buf2, size};
+ std::free(buf2);
+ return out;
+ }
+ std::free(buf2);
+ }
+ ReportError(r);
+ return std::string{};
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Poll.cpp b/wpiutil/src/main/native/cpp/uv/Poll.cpp
new file mode 100644
index 0000000..8b608cb
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Poll.cpp
@@ -0,0 +1,87 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Poll.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Poll> Poll::Create(Loop& loop, int fd) {
+ auto h = std::make_shared<Poll>(private_init{});
+ int err = uv_poll_init(loop.GetRaw(), h->GetRaw(), fd);
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+std::shared_ptr<Poll> Poll::CreateSocket(Loop& loop, uv_os_sock_t sock) {
+ auto h = std::make_shared<Poll>(private_init{});
+ int err = uv_poll_init_socket(loop.GetRaw(), h->GetRaw(), sock);
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Poll::Reuse(int fd, std::function<void()> callback) {
+ if (IsClosing()) return;
+ if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
+ m_reuseData->callback = callback;
+ m_reuseData->isSocket = false;
+ m_reuseData->fd = fd;
+ uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+ Poll& h = *static_cast<Poll*>(handle->data);
+ if (!h.m_reuseData || h.m_reuseData->isSocket) return; // just in case
+ auto data = std::move(h.m_reuseData);
+ int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->fd);
+ if (err < 0) {
+ h.ReportError(err);
+ return;
+ }
+ data->callback();
+ });
+}
+
+void Poll::ReuseSocket(uv_os_sock_t sock, std::function<void()> callback) {
+ if (IsClosing()) return;
+ if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
+ m_reuseData->callback = callback;
+ m_reuseData->isSocket = true;
+ m_reuseData->sock = sock;
+ uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+ Poll& h = *static_cast<Poll*>(handle->data);
+ if (!h.m_reuseData || !h.m_reuseData->isSocket) return; // just in case
+ auto data = std::move(h.m_reuseData);
+ int err = uv_poll_init(h.GetLoopRef().GetRaw(), h.GetRaw(), data->sock);
+ if (err < 0) {
+ h.ReportError(err);
+ return;
+ }
+ data->callback();
+ });
+}
+
+void Poll::Start(int events) {
+ Invoke(&uv_poll_start, GetRaw(), events,
+ [](uv_poll_t* handle, int status, int events) {
+ Poll& h = *static_cast<Poll*>(handle->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.pollEvent(events);
+ });
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Prepare.cpp b/wpiutil/src/main/native/cpp/uv/Prepare.cpp
new file mode 100644
index 0000000..f27f477
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Prepare.cpp
@@ -0,0 +1,34 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Prepare.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Prepare> Prepare::Create(Loop& loop) {
+ auto h = std::make_shared<Prepare>(private_init{});
+ int err = uv_prepare_init(loop.GetRaw(), h->GetRaw());
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Prepare::Start() {
+ Invoke(&uv_prepare_start, GetRaw(), [](uv_prepare_t* handle) {
+ Prepare& h = *static_cast<Prepare*>(handle->data);
+ h.prepare();
+ });
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Process.cpp b/wpiutil/src/main/native/cpp/uv/Process.cpp
new file mode 100644
index 0000000..5778965
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Process.cpp
@@ -0,0 +1,128 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Process.h"
+
+#include "wpi/SmallString.h"
+#include "wpi/uv/Loop.h"
+#include "wpi/uv/Pipe.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Process> Process::SpawnArray(Loop& loop, const Twine& file,
+ ArrayRef<Option> options) {
+ // convert Option array to libuv structure
+ uv_process_options_t coptions;
+
+ coptions.exit_cb = [](uv_process_t* handle, int64_t status, int signal) {
+ auto& h = *static_cast<Process*>(handle->data);
+ h.exited(status, signal);
+ };
+
+ SmallString<128> fileBuf;
+ coptions.file = file.toNullTerminatedStringRef(fileBuf).data();
+ coptions.cwd = nullptr;
+ coptions.flags = 0;
+ coptions.uid = 0;
+ coptions.gid = 0;
+
+ SmallVector<char*, 4> argsBuf;
+ SmallVector<char*, 4> envBuf;
+ struct StdioContainer : public uv_stdio_container_t {
+ StdioContainer() {
+ flags = UV_IGNORE;
+ data.fd = 0;
+ }
+ };
+ SmallVector<StdioContainer, 4> stdioBuf;
+
+ for (auto&& o : options) {
+ switch (o.m_type) {
+ case Option::kArg:
+ argsBuf.push_back(const_cast<char*>(o.m_data.str));
+ break;
+ case Option::kEnv:
+ envBuf.push_back(const_cast<char*>(o.m_data.str));
+ break;
+ case Option::kCwd:
+ coptions.cwd = o.m_data.str[0] == '\0' ? nullptr : o.m_data.str;
+ break;
+ case Option::kUid:
+ coptions.uid = o.m_data.uid;
+ coptions.flags |= UV_PROCESS_SETUID;
+ break;
+ case Option::kGid:
+ coptions.gid = o.m_data.gid;
+ coptions.flags |= UV_PROCESS_SETGID;
+ break;
+ case Option::kSetFlags:
+ coptions.flags |= o.m_data.flags;
+ break;
+ case Option::kClearFlags:
+ coptions.flags &= ~o.m_data.flags;
+ break;
+ case Option::kStdioIgnore: {
+ size_t index = o.m_data.stdio.index;
+ if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ stdioBuf[index].flags = UV_IGNORE;
+ stdioBuf[index].data.fd = 0;
+ break;
+ }
+ case Option::kStdioInheritFd: {
+ size_t index = o.m_data.stdio.index;
+ if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ stdioBuf[index].flags = UV_INHERIT_FD;
+ stdioBuf[index].data.fd = o.m_data.stdio.fd;
+ break;
+ }
+ case Option::kStdioInheritPipe: {
+ size_t index = o.m_data.stdio.index;
+ if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ stdioBuf[index].flags = UV_INHERIT_STREAM;
+ stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
+ break;
+ }
+ case Option::kStdioCreatePipe: {
+ size_t index = o.m_data.stdio.index;
+ if (index >= stdioBuf.size()) stdioBuf.resize(index + 1);
+ stdioBuf[index].flags =
+ static_cast<uv_stdio_flags>(UV_CREATE_PIPE | o.m_data.stdio.flags);
+ stdioBuf[index].data.stream = o.m_data.stdio.pipe->GetRawStream();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (argsBuf.empty()) argsBuf.push_back(const_cast<char*>(coptions.file));
+ argsBuf.push_back(nullptr);
+ coptions.args = argsBuf.data();
+
+ if (envBuf.empty()) {
+ coptions.env = nullptr;
+ } else {
+ envBuf.push_back(nullptr);
+ coptions.env = envBuf.data();
+ }
+
+ coptions.stdio_count = stdioBuf.size();
+ coptions.stdio = static_cast<uv_stdio_container_t*>(stdioBuf.data());
+
+ auto h = std::make_shared<Process>(private_init{});
+ int err = uv_spawn(loop.GetRaw(), h->GetRaw(), &coptions);
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Signal.cpp b/wpiutil/src/main/native/cpp/uv/Signal.cpp
new file mode 100644
index 0000000..083b852
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Signal.cpp
@@ -0,0 +1,36 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Signal.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Signal> Signal::Create(Loop& loop) {
+ auto h = std::make_shared<Signal>(private_init{});
+ int err = uv_signal_init(loop.GetRaw(), h->GetRaw());
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Signal::Start(int signum) {
+ Invoke(&uv_signal_start, GetRaw(),
+ [](uv_signal_t* handle, int signum) {
+ Signal& h = *static_cast<Signal*>(handle->data);
+ h.signal(signum);
+ },
+ signum);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Stream.cpp b/wpiutil/src/main/native/cpp/uv/Stream.cpp
new file mode 100644
index 0000000..b1fd294
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Stream.cpp
@@ -0,0 +1,106 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Stream.h"
+
+#include "wpi/SmallVector.h"
+
+using namespace wpi;
+using namespace wpi::uv;
+
+namespace {
+class CallbackWriteReq : public WriteReq {
+ public:
+ CallbackWriteReq(ArrayRef<Buffer> bufs,
+ std::function<void(MutableArrayRef<Buffer>, Error)> callback)
+ : m_bufs{bufs.begin(), bufs.end()} {
+ finish.connect([=](Error err) { callback(m_bufs, err); });
+ }
+
+ private:
+ SmallVector<Buffer, 4> m_bufs;
+};
+} // namespace
+
+namespace wpi {
+namespace uv {
+
+ShutdownReq::ShutdownReq() {
+ error = [this](Error err) { GetStream().error(err); };
+}
+
+WriteReq::WriteReq() {
+ error = [this](Error err) { GetStream().error(err); };
+}
+
+void Stream::Shutdown(const std::shared_ptr<ShutdownReq>& req) {
+ if (Invoke(&uv_shutdown, req->GetRaw(), GetRawStream(),
+ [](uv_shutdown_t* req, int status) {
+ auto& h = *static_cast<ShutdownReq*>(req->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.complete();
+ h.Release(); // this is always a one-shot
+ }))
+ req->Keep();
+}
+
+void Stream::Shutdown(std::function<void()> callback) {
+ auto req = std::make_shared<ShutdownReq>();
+ if (callback) req->complete.connect(callback);
+ Shutdown(req);
+}
+
+void Stream::StartRead() {
+ Invoke(&uv_read_start, GetRawStream(), &Handle::AllocBuf,
+ [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
+ auto& h = *static_cast<Stream*>(stream->data);
+ Buffer data = *buf;
+
+ // nread=0 is simply ignored
+ if (nread == UV_EOF)
+ h.end();
+ else if (nread > 0)
+ h.data(data, static_cast<size_t>(nread));
+ else if (nread < 0)
+ h.ReportError(nread);
+
+ // free the buffer
+ h.FreeBuf(data);
+ });
+}
+
+void Stream::Write(ArrayRef<Buffer> bufs,
+ const std::shared_ptr<WriteReq>& req) {
+ if (Invoke(&uv_write, req->GetRaw(), GetRawStream(), bufs.data(), bufs.size(),
+ [](uv_write_t* r, int status) {
+ auto& h = *static_cast<WriteReq*>(r->data);
+ if (status < 0) h.ReportError(status);
+ h.finish(Error(status));
+ h.Release(); // this is always a one-shot
+ }))
+ req->Keep();
+}
+
+void Stream::Write(
+ ArrayRef<Buffer> bufs,
+ std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
+ Write(bufs, std::make_shared<CallbackWriteReq>(bufs, callback));
+}
+
+int Stream::TryWrite(ArrayRef<Buffer> bufs) {
+ int val = uv_try_write(GetRawStream(), bufs.data(), bufs.size());
+ if (val < 0) {
+ this->ReportError(val);
+ return 0;
+ }
+ return val;
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Tcp.cpp b/wpiutil/src/main/native/cpp/uv/Tcp.cpp
new file mode 100644
index 0000000..f71e055
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Tcp.cpp
@@ -0,0 +1,155 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Tcp.h"
+
+#include <cstring>
+
+#include "wpi/uv/util.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Tcp> Tcp::Create(Loop& loop, unsigned int flags) {
+ auto h = std::make_shared<Tcp>(private_init{});
+ int err = uv_tcp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Tcp::Reuse(std::function<void()> callback, unsigned int flags) {
+ if (IsClosing()) return;
+ if (!m_reuseData) m_reuseData = std::make_unique<ReuseData>();
+ m_reuseData->callback = callback;
+ m_reuseData->flags = flags;
+ uv_close(GetRawHandle(), [](uv_handle_t* handle) {
+ Tcp& h = *static_cast<Tcp*>(handle->data);
+ if (!h.m_reuseData) return; // just in case
+ auto data = std::move(h.m_reuseData);
+ int err = uv_tcp_init_ex(h.GetLoopRef().GetRaw(), h.GetRaw(), data->flags);
+ if (err < 0) {
+ h.ReportError(err);
+ return;
+ }
+ data->callback();
+ });
+}
+
+std::shared_ptr<Tcp> Tcp::Accept() {
+ auto client = Create(GetLoopRef());
+ if (!client) return nullptr;
+ if (!Accept(client)) {
+ client->Release();
+ return nullptr;
+ }
+ return client;
+}
+
+Tcp* Tcp::DoAccept() { return Accept().get(); }
+
+void Tcp::Bind(const Twine& ip, unsigned int port, unsigned int flags) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+void Tcp::Bind6(const Twine& ip, unsigned int port, unsigned int flags) {
+ sockaddr_in6 addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+sockaddr_storage Tcp::GetSock() {
+ sockaddr_storage name;
+ int len = sizeof(name);
+ if (!Invoke(&uv_tcp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+ &len))
+ std::memset(&name, 0, sizeof(name));
+ return name;
+}
+
+sockaddr_storage Tcp::GetPeer() {
+ sockaddr_storage name;
+ int len = sizeof(name);
+ if (!Invoke(&uv_tcp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+ &len))
+ std::memset(&name, 0, sizeof(name));
+ return name;
+}
+
+void Tcp::Connect(const sockaddr& addr,
+ const std::shared_ptr<TcpConnectReq>& req) {
+ if (Invoke(&uv_tcp_connect, req->GetRaw(), GetRaw(), &addr,
+ [](uv_connect_t* req, int status) {
+ auto& h = *static_cast<TcpConnectReq*>(req->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.connected();
+ h.Release(); // this is always a one-shot
+ }))
+ req->Keep();
+}
+
+void Tcp::Connect(const sockaddr& addr, std::function<void()> callback) {
+ auto req = std::make_shared<TcpConnectReq>();
+ req->connected.connect(callback);
+ Connect(addr, req);
+}
+
+void Tcp::Connect(const Twine& ip, unsigned int port,
+ const std::shared_ptr<TcpConnectReq>& req) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Connect(reinterpret_cast<const sockaddr&>(addr), req);
+}
+
+void Tcp::Connect(const Twine& ip, unsigned int port,
+ std::function<void()> callback) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Connect(reinterpret_cast<const sockaddr&>(addr), callback);
+}
+
+void Tcp::Connect6(const Twine& ip, unsigned int port,
+ const std::shared_ptr<TcpConnectReq>& req) {
+ sockaddr_in6 addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Connect(reinterpret_cast<const sockaddr&>(addr), req);
+}
+
+void Tcp::Connect6(const Twine& ip, unsigned int port,
+ std::function<void()> callback) {
+ sockaddr_in6 addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Connect(reinterpret_cast<const sockaddr&>(addr), callback);
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Timer.cpp b/wpiutil/src/main/native/cpp/uv/Timer.cpp
new file mode 100644
index 0000000..749d9a8
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Timer.cpp
@@ -0,0 +1,46 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Timer.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Timer> Timer::Create(Loop& loop) {
+ auto h = std::make_shared<Timer>(private_init{});
+ int err = uv_timer_init(loop.GetRaw(), h->GetRaw());
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Timer::SingleShot(Loop& loop, Time timeout, std::function<void()> func) {
+ auto h = Create(loop);
+ if (!h) return;
+ h->timeout.connect([theTimer = h.get(), func]() {
+ func();
+ theTimer->Close();
+ });
+ h->Start(timeout);
+}
+
+void Timer::Start(Time timeout, Time repeat) {
+ Invoke(&uv_timer_start, GetRaw(),
+ [](uv_timer_t* handle) {
+ Timer& h = *static_cast<Timer*>(handle->data);
+ h.timeout();
+ },
+ timeout.count(), repeat.count());
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Tty.cpp b/wpiutil/src/main/native/cpp/uv/Tty.cpp
new file mode 100644
index 0000000..cdd6fd5
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Tty.cpp
@@ -0,0 +1,27 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Tty.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+std::shared_ptr<Tty> Tty::Create(Loop& loop, uv_file fd, bool readable) {
+ auto h = std::make_shared<Tty>(private_init{});
+ int err = uv_tty_init(loop.GetRaw(), h->GetRaw(), fd, readable ? 1 : 0);
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Udp.cpp b/wpiutil/src/main/native/cpp/uv/Udp.cpp
new file mode 100644
index 0000000..2c0d29f
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Udp.cpp
@@ -0,0 +1,176 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Udp.h"
+
+#include <cstring>
+
+#include "wpi/SmallString.h"
+#include "wpi/SmallVector.h"
+#include "wpi/uv/util.h"
+
+namespace {
+
+using namespace wpi;
+using namespace wpi::uv;
+
+class CallbackUdpSendReq : public UdpSendReq {
+ public:
+ CallbackUdpSendReq(
+ ArrayRef<Buffer> bufs,
+ std::function<void(MutableArrayRef<Buffer>, Error)> callback)
+ : m_bufs{bufs.begin(), bufs.end()} {
+ complete.connect([=](Error err) { callback(m_bufs, err); });
+ }
+
+ private:
+ SmallVector<Buffer, 4> m_bufs;
+};
+
+} // namespace
+
+namespace wpi {
+namespace uv {
+
+UdpSendReq::UdpSendReq() {
+ error = [this](Error err) { GetUdp().error(err); };
+}
+
+std::shared_ptr<Udp> Udp::Create(Loop& loop, unsigned int flags) {
+ auto h = std::make_shared<Udp>(private_init{});
+ int err = uv_udp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
+ if (err < 0) {
+ loop.ReportError(err);
+ return nullptr;
+ }
+ h->Keep();
+ return h;
+}
+
+void Udp::Bind(const Twine& ip, unsigned int port, unsigned int flags) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+void Udp::Bind6(const Twine& ip, unsigned int port, unsigned int flags) {
+ sockaddr_in6 addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Bind(reinterpret_cast<const sockaddr&>(addr), flags);
+}
+
+void Udp::Connect(const Twine& ip, unsigned int port) {
+ sockaddr_in addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Connect(reinterpret_cast<const sockaddr&>(addr));
+}
+
+void Udp::Connect6(const Twine& ip, unsigned int port) {
+ sockaddr_in6 addr;
+ int err = NameToAddr(ip, port, &addr);
+ if (err < 0)
+ ReportError(err);
+ else
+ Connect(reinterpret_cast<const sockaddr&>(addr));
+}
+
+sockaddr_storage Udp::GetPeer() {
+ sockaddr_storage name;
+ int len = sizeof(name);
+ if (!Invoke(&uv_udp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+ &len))
+ std::memset(&name, 0, sizeof(name));
+ return name;
+}
+
+sockaddr_storage Udp::GetSock() {
+ sockaddr_storage name;
+ int len = sizeof(name);
+ if (!Invoke(&uv_udp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
+ &len))
+ std::memset(&name, 0, sizeof(name));
+ return name;
+}
+
+void Udp::SetMembership(const Twine& multicastAddr, const Twine& interfaceAddr,
+ uv_membership membership) {
+ SmallString<128> multicastAddrBuf;
+ SmallString<128> interfaceAddrBuf;
+ Invoke(&uv_udp_set_membership, GetRaw(),
+ multicastAddr.toNullTerminatedStringRef(multicastAddrBuf).data(),
+ interfaceAddr.toNullTerminatedStringRef(interfaceAddrBuf).data(),
+ membership);
+}
+
+void Udp::SetMulticastInterface(const Twine& interfaceAddr) {
+ SmallString<128> interfaceAddrBuf;
+ Invoke(&uv_udp_set_multicast_interface, GetRaw(),
+ interfaceAddr.toNullTerminatedStringRef(interfaceAddrBuf).data());
+}
+
+void Udp::Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
+ const std::shared_ptr<UdpSendReq>& req) {
+ if (Invoke(&uv_udp_send, req->GetRaw(), GetRaw(), bufs.data(), bufs.size(),
+ &addr, [](uv_udp_send_t* r, int status) {
+ auto& h = *static_cast<UdpSendReq*>(r->data);
+ if (status < 0) h.ReportError(status);
+ h.complete(Error(status));
+ h.Release(); // this is always a one-shot
+ }))
+ req->Keep();
+}
+
+void Udp::Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
+ std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
+ Send(addr, bufs, std::make_shared<CallbackUdpSendReq>(bufs, callback));
+}
+
+void Udp::Send(ArrayRef<Buffer> bufs, const std::shared_ptr<UdpSendReq>& req) {
+ if (Invoke(&uv_udp_send, req->GetRaw(), GetRaw(), bufs.data(), bufs.size(),
+ nullptr, [](uv_udp_send_t* r, int status) {
+ auto& h = *static_cast<UdpSendReq*>(r->data);
+ if (status < 0) h.ReportError(status);
+ h.complete(Error(status));
+ h.Release(); // this is always a one-shot
+ }))
+ req->Keep();
+}
+
+void Udp::Send(ArrayRef<Buffer> bufs,
+ std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
+ Send(bufs, std::make_shared<CallbackUdpSendReq>(bufs, callback));
+}
+
+void Udp::StartRecv() {
+ Invoke(&uv_udp_recv_start, GetRaw(), &AllocBuf,
+ [](uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf,
+ const sockaddr* addr, unsigned flags) {
+ auto& h = *static_cast<Udp*>(handle->data);
+ Buffer data = *buf;
+
+ // nread=0 is simply ignored
+ if (nread > 0)
+ h.received(data, static_cast<size_t>(nread), *addr, flags);
+ else if (nread < 0)
+ h.ReportError(nread);
+
+ // free the buffer
+ h.FreeBuf(data);
+ });
+}
+
+} // namespace uv
+} // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/uv/Work.cpp b/wpiutil/src/main/native/cpp/uv/Work.cpp
new file mode 100644
index 0000000..d71ef81
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/uv/Work.cpp
@@ -0,0 +1,48 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "wpi/uv/Work.h"
+
+#include "wpi/uv/Loop.h"
+
+namespace wpi {
+namespace uv {
+
+WorkReq::WorkReq() {
+ error = [this](Error err) { GetLoop().error(err); };
+}
+
+void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req) {
+ int err = uv_queue_work(loop.GetRaw(), req->GetRaw(),
+ [](uv_work_t* req) {
+ auto& h = *static_cast<WorkReq*>(req->data);
+ h.work();
+ },
+ [](uv_work_t* req, int status) {
+ auto& h = *static_cast<WorkReq*>(req->data);
+ if (status < 0)
+ h.ReportError(status);
+ else
+ h.afterWork();
+ h.Release(); // this is always a one-shot
+ });
+ if (err < 0)
+ loop.ReportError(err);
+ else
+ req->Keep();
+}
+
+void QueueWork(Loop& loop, std::function<void()> work,
+ std::function<void()> afterWork) {
+ auto req = std::make_shared<WorkReq>();
+ if (work) req->work.connect(work);
+ if (afterWork) req->afterWork.connect(afterWork);
+ QueueWork(loop, req);
+}
+
+} // namespace uv
+} // namespace wpi