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 &current_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 << "&amp;";
+    else if (C == '<')
+      Out << "&lt;";
+    else if (C == '>')
+      Out << "&gt;";
+    else if (C == '\"')
+      Out << "&quot;";
+    else if (C == '\'')
+      Out << "&apos;";
+    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