Squashed 'third_party/allwpilib/' changes from e473a00f97..e4b91005cf

e4b91005cf [examples] Update SwerveModule constructor doc (NFC) (#4042)
a260bfd83b [examples] Remove "this" keyword from SwerveModule (#4043)
18e262a100 [examples] Fix multiple doc typos in SwerveControllerCommand example (NFC) (#4044)
4bd1f526ab [wpilibc] Prevent StopMotor from terminating robot during MotorSafety check (#4038)
27847d7eb2 [sim] Expose GUI control functions via HAL_RegisterExtension (#4034)
b2a8d3f0f3 [wpilibc] Add mechanism to reset MotorSafety list (#4037)
49adac9564 [wpilib] Check for signedness in ArcadeDriveIK() (#4028)
a19d1133b1 [wpiutil] libuv: Fix sign compare warnings in gcc 11.2 (#4031)
dde91717e4 [build] cmake: Add ability to customize target warnings (#4032)
e9050afd67 [sim] Update sim match time to match real robot (#4024)
165d2837cf [wpilib] Preferences: Set Persistent in Init methods (#4025)
ac7549edca [glass] Fix snprintf truncation warning (#4029)
4d96bc72e0 [wpilibj] Fix typos in error messages for non-null assertions (#4014)
3411eee20f [hal] Replace hardcoded sim array sizes with constants (#4015)
74de97eeca [wpilibc] Add mechanism to reset various global structures (#4007)
4e3cc25012 [examples] Fix periodic function rate comment (NFC) (#4013)
90c1db393e [sim] Add exported functions to control the sim GUI (#3995)
2f43274aa4 [wpilibj] MechanismRoot2d: Add flush to setPosition (#4011)
aeca09db09 [glass] Support remapping of Enter key (#3994)
c107f22c67 [sim] Sim GUI: don't force-show Timing and Other Devices (#4001)
68fe51e8da [wpigui] Update PFD to latest, fix kdialog multiselect (#4005)
8d08d67cf1 [wpigui] PFD: Add console warning if file chooser unavailable (#4003)
4f1782f66e [wpilibc] Only call HAL_Report when initializing SmartDashboard (#4006)
3f77725cd3 Remove uses of iostream (#4004)
5635f33a32 [glass] Increase plot depth to 20K points (#3993)
bca4b7111b [glass] Fix PlotSeries::SetSource() (#3991)
6a6366b0d6 [commands] Add until() as alias for withInterrupt() (#3981)
16bf2c70c5 [wpilib] Fix joystick out of range error messages (#3988)
4b3edb742c [wpilib] Fix ADIS16448 IMU default constructor not working in Java (#3989)
fcf23fc9e9 [hal] Fix potential gamedata out of bounds read (#3983)
af5ef510c5 [wpilibc] Fix REV PH pulse duration units (#3982)
05401e2b81 [wpilib] Write REV PH firmware version to roboRIO to display on driver station (#3977)
9fde0110b6 Update to 2022 v4.0 image (#3944)
b03f8ddb2e [examples] fix incorrect variable in Arm Simulation Pref (#3980)
a26df2a022 [examples] Update ArmSimulation example to use Preferences (#3976)
d68d6674e8 [examples] Armbot: rename kCos to kG (#3975)
a8f0f6bb90 [wpilibj] Fix ADIS16448 getRate to return rate instead of angle (#3974)
dd9c92d5bf [build] Remove debug info from examples (#3971)
84df14dd70 [rtns] Fix icons (#3972)
560094ad92 [examples] Correct Mecanum example axes (#3955)
7ea1be9c01 [wpilibc] Fix typo in hardware version for REV PDH (#3969)
700f13bffd [wpilibj] Make methods public for Java REV PDH (#3970)
b6aa7c1aa9 [wpilibj] Make methods public for Java REVPH (#3968)
eb4d183e48 [wpimath] Fix clang-tidy bugprone-integer-division warning (#3966)
77e4e81e1e [wpilib] Add Field widget to BuiltInWidgets in shuffleboard (#3961)
88f5cb6eb0 [build] Publish PDBs with C++ tools (#3960)
efae552f3e [wpimath] Remove DifferentialDriveKinematics include from odometry (#3958)
46b277421a [glass] Update Speed Controller Type name for 2022 WPILib (#3952)
42908126b9 [wpilib] Add DCMotorSim (#3910)
a467392cbd [wpiutil] StackTrace: Add ability to override default implementation (#3951)
78d0bcf49d [templates] Add SimulationInit()/SimulationPeriodic() to robot templates (#3943)
02a0ced9b0 [wpilib] MecanumDrive: update docs for axis to match implementation (NFC) (#3942)
4ccfe1c9f2 [wpilib] Added docs clarification on units for drive class WheelSpeeds (NFC) (#3939)
830c0c5c2f [wpilib] MechanismLigament2d: Add getters for color and line weight (#3947)
5548a37465 [wpilib] PowerDistribution: Add module type getter (#3948)
2f9a600de2 [hal] Fix PCM one shot (#3949)
559db11a20 [myRobot] Skip deploying debug libraries for myRobot deploys (#3950)
76c78e295b [examples] Reorder SwerveModules in SwerveControllerCommand example odometry update (#3934)
debbd5ff4b [wpilib] Improve PowerDistribution docs (NFC) (#3925)
841174f302 [commands] Change command vendordep JSON version number to 1.0.0 (#3938)
8c55844f91 [wpilib] Remove comment about Mecanum right side inverted (NFC) (#3929)
0b990bf0f5 [hal] Fix PCM sticky faults clear function crashing (#3932)
104d7e2abc [hal] Don't throw exceptions in PCM JNI (#3933)
5ba69e1af1 [examples] Updated type in Java SwerveModule (#3928)
f3a0b5c7d7 [wpimath] Fix Java SimpleMotorFeedforward Docs (NFC) (#3926)
7f4265facc [wpimath] Add LinearFilter::FiniteDifference() (#3900)
63d1fb3bed [wpiutil] Modify fmt to not throw on write failure (#3919)
36af6d25a5 [wpimath] Fix input vector in pose estimator docs (NFC) (#3923)
8f387f7255 [wpilibj] Switch ControlWord mutex to actual reentrant mutex (#3922)
792e735e08 [wpimath] Move TrajectoryGenerator::SetErrorHandler definition to .cpp (#3920)
3b76de83eb [commands] Fix ProfiledPIDCommand use-after-free (#3904)
ad9f738cfa [fieldimages] Fix maven publishing (#3897)
49455199e5 [examples] Use left/rightGroup.Get() for simulator inputs to fix inversions (#3908)
64426502ea [wpimath] Fix arm -> flywheel typo (NFC) (#3911)
8cc112d196 [wpiutil] Fix wpi::array for move-only types (#3917)
e78cd49861 [build] Upgrade Java formatter plugins (#3894)
cfb4f756d6 [build] Upgrade to shadow 7.1.2 (#3893)
ba0908216c [wpimath] Fix crash in KF latency compensator (#3888)
a3a0334fad [build] cmake: Move fieldImages to WITH_GUI (#3885)
cf7460c3a8 [fieldImages] Add 2022 field (#3883)
db0fbb6448 [wpimath] Fix LQR matrix constructor overload for Q, R, and N (#3884)
8ac45f20bb [commands] Update Command documentation (NFC) (#3881)
b3707cca0b [wpiutil] Upgrade to fmt 8.1.1 (#3879)
a69ee3ece9 [wpimath] Const-qualify Twist2d scalar multiply (#3882)
750d9a30c9 [examples] Fix Eigen out of range error when running example (#3877)
41c5b2b5ac [rtns] Add cmake build (#3866)
6cf3f9b28e [build] Upgrade to Gradle 7.3.3 (#3878)
269cf03472 [examples] Add communication examples (e.g. arduino) (#2500)
5ccfc4adbd [oldcommands] Deprecate PIDWrappers, since they use deprecated interfaces (#3868)
b6f44f98be [hal] Add warning about onboard I2C (#3871)
0dca57e9ec [templates] romieducational: Invert drivetrain and disable motor safety (#3869)
22c4da152e [wpilib] Add GetRate() to ADIS classes (#3864)
05d66f862d [templates] Change the template ordering to put command based first (#3863)
b09f5b2cf2 [wpilibc] Add virtual dtor for LinearSystemSim (#3861)
a2510aaa0e [wpilib] Make ADIS IMU classes unit-safe (#3860)
947f589916 [wpilibc] Rename ADIS_16470_IMU.cpp to match class name (#3859)
bbd8980a20 [myRobot] Fix cameraserver library order (#3858)
831052f118 [wpilib] Add simulation support to ADIS classes (#3857)
c137569f91 [wpilib] Throw exception if the REV Pneumatic Hub firmware version is older than 22.0.0 (#3853)
dae61226fa Fix Maven Artifacts readme (#3856)
3ad4594a88 Update Maven artifacts readme for 2022 (#3855)
112acb9a62 [wpilibc] Move ADIS IMU constants to inside class (#3852)
ecee224e81 [wpilib] Allow SendableCameraWrappers to take arbitrary URLs (#3850)
a3645dea34 LICENSE: Bump year range to include 2022 (#3854)
7c09f44898 [wpilib] Use PSI for compressor config and sensor reading (#3847)
f401ea9aae [wpigui] Remove wpiutil linkage (#3851)
bf8517f1e6 [wpimath] TimeInterpolatableBufferTest: Fix lint warnings (#3849)
528087e308 [hal] Use enums with fixed underlying type in clang C (#3297)
1f59ff72f9 [wpilib] Add ADIS IMUs (#3777)
315be873c4 [wpimath] Add TimeInterpolatableBuffer (#2695)
b8d019cdb4 [wpilib] Rename NormalizeWheelSpeeds to DesaturateWheelSpeeds (#3791)
102f23bbdb [wpilibj] DriverStation: Set thread interrupted state (#3846)
b85c24a79c [wpilib] Add warning about onboard I2C (#3842)
eee29daaf9 [newCommands] Trigger: Allow override of debounce type (#3845)
aa9dfabde2 [wpimath] Move debouncer to filters (#3838)
5999a26fba [wpiutil] Add GetSystemTime() (#3840)
1e82595ffb [examples] Fix arcade inversions (#3841)
e373fa476b [wpiutil] Add disableMockTime to JNI (#3839)
dceb5364f4 [examples] Ensure right side motors are inverted (#3836)
baacbc8e24 [wpilib] Tachometer: Add function to return RPS (#3833)
84b15f0883 [templates] Add Java Romi Educational template (#3837)
c0da9d2d35 [examples] Invert Right Motor in Romi Java examples (#3828)
0fe0be2733 [build] Change project year to intellisense (#3835)
eafa947338 [wpimath] Make copies of trajectory constraint arguments (#3832)
9d13ae8d01 [wpilib] Add notes for Servo get that it only returns cmd (NFC) (#3820)
2a64e4bae5 [wpimath] Give drivetrain a more realistic width in TrajectoryJsonTest.java (#3822)
c3fd20db59 [wpilib] Fix trajectory sampling in DifferentialDriveSim test (#3821)
6f91f37cd0 [examples] Fix SwerveControllerCommand order of Module States (#3815)
5158730b81 [wpigui] Upgrade to imgui 1.86, GLFW 3.3.6 (#3817)
2ad2d2ca96 [wpiutil] MulticastServiceResolver: Fix C array returning functions (#3816)
b5fd29774f [wpilibj] Trigger: implement BooleanSupplier interface (#3811)
9f8f330e96 [wpilib] Fix Mecanum and SwerveControllerCommand when desired rotation passed (#3808)
1ad3b1b333 [hal] Don't copy byte to where null terminator goes (#3807)
dfc24425c3 [build] Fix gazebo gradle IDE warnings (#3806)
c02577bb51 [glass] Configure delay loading for windows camera server support (#3803)
c9e6a96a61 [wpilib] Document range of Servo angle (NFC) (#3796)
9778626f34 [wpilib, hal] Add support for getting faults and versions from power distribution (#3794)
34b2d0dae1 [wpilib, hal] High Level REV PH changes (#3792)
59a7528fd6 [cscore] Fix crash when usbcamera is deleted before message pump thread fully starts (#3804)
11d9859ef1 [build] Update plugins to remove log4j vulnerabilities (#3805)
e44ed752ad [glass] Fix CollapsingHeader in Encoder, PCM, and DeviceTree (#3797)
52b2dd5b89 [build] Bump native utils to remove log4j (#3802)
c46636f218 [wpilib] Improve new counter classes documentation (NFC) (#3801)
dc531462e1 [build] Update to gradle 7.3.2 (#3800)
92ba98621c [wpimath] Add helper variable templates for units type traits (#3790)
d41d051f1b [wpilibc] Fix Mecanum & Swerve ControllerCommand lambda capture (#3795)
c5ae0effac OtherVersions.md: Add one missing case of useLocal (#3788)
b3974c6ed3 [wpimath] Upgrade to Drake v0.37.0 (#3786)
589a00e379 [wpilibc] Start DriverStation thread from RobotBase (#3785)
8d9836ca02 [wpilib] Improve curvature drive documentation (NFC) (#3783)
8b5bf8632e [myRobot] Add wpimath and wpiutil JNI (#3784)
1846114491 [examples] Update references from characterization to SysId (NFC) (#3782)
2c461c794e [build] Update to gradle 7.3 (#3778)
109363daa4 [hal] Add remaining driver functions for REVPH (#3776)
41d26bee8d [hal] Refactor REV PDH (#3775)
7269a170fb Upgrade maven deps to latest versions and fix new linter errors (#3772)
441f2ed9b0 [build] actions: use fixed image versions instead latest (#3761)
15275433d4 [examples] Fix duplicate port allocations in C++ SwerveBot/SwerveDrivePoseEstimator/RomiReference (#3773)
1ac02d2f58 [examples] Fix drive Joystick axes in several examples (#3769)
8ee6257e92 [wpilib] DifferentialDrivetrainSim.KitbotMotor: Add NEO and Falcon 500 (#3762)
d81ef2bc5c [wpilib] Fix deadlocks in Mechanism2d et al. (#3770)
acb64dff97 [wpimath] Make RamseteController::Calculate() more concise (#3763)
3f6cf76a8c [hal] Refactor REV PH CAN frames (#3756)
3ef2dab465 [wpilib] DutyCycleEncoder: add setting of duty cycle range (#3759)
a5a56dd067 Readme: Add Visual Studio 2022 (#3760)
04957a6d30 [wpimath] Fix units of RamseteController's b and zeta (#3757)
5da54888f8 [glass] Upgrade imgui to 0.85, implot to HEAD, glfw to 3.3.5 (#3754)
6c93365b0f [wpiutil] MulticastService cleanup (#3750)
1c4a8bfb66 [cscore] Cleanup Windows USB camera impl (#3751)
d51a1d3b3d [rtns] Fix icon (#3749)
aced2e7da6 Add roboRIO Team Number Setter tool (#3744)
fa1ceca83a [wpilibj] Use DS cache for iterative robot control word cache (#3748)
0ea05d34e6 [build] Update to gradle 7.2 (#3746)
09db4f672b [build] Update to native utils 2022.6.1 (#3745)
4ba80a3a8c [wpigui] Don't recursively render frames in size callback (#3743)
ae208d2b17 [wpiutil] StringExtras: Add substr() (#3742)
6f51cb3b98 [wpiutil] MulticastResolver: make event manual reset, change to multiple read (#3736)
f6159ee1a2 [glass] Fix Drive widget handling of negative rotation (#3739)
7f401ae895 [build] Update NI libraries to 2022.2.3 (#3738)
0587b7043a [glass] Use JSON files for storage instead of imgui ini
0bbf51d566 [wpigui] Change maximized to bool
92c6eae6b0 [wpigui] PFD: Add explicit to constructors
141354cd79 [wpigui] Add hooks for custom load/save settings
f6e9fc7d71 [wpiutil] Handle multicast service collision on linux (#3734)
d8418be7d1 [glass, outlineviewer] Return 0 from WinMain (#3735)
82066946e5 [wpiutil] Add mDNS resolver and announcer (#3733)
4b1defc8d8 [wpilib] Remove automatic PD type from module type enum (#3732)
da90c1cd2c [wpilib] Add bang-bang controller (#3676)
3aa54fa027 [wpilib] Add new counter implementations (#2447)
b156db400d [hal, wpilib] Incorporate pneumatic control type into wpilibc/j (#3728)
9aba2b7583 [oldCommands] Add wrappers for WPILib objects to work with old PID Controller (#3710)
a9931223f0 [hal] Add REV PH faults (#3729)
aacf9442e4 [wpimath] Fix units typo in LinearSystemId source comment (#3730)
7db10ecf00 [wpilibc] Make SPI destructor virtual since SPI contains virtual functions (#3727)
a0a5b2aea5 [wpimath] Upgrade to EJML 0.41 (#3726)
eb835598a4 [hal] Add HAL functions for compressor config modes on REV PH (#3724)
f0ab6df5b6 [wpimath] Upgrade to Drake v0.36.0 (#3722)
075144faa3 [docs] Parse files without extensions with Doxygen (#3721)
32468a40cb [hal] Remove use of getDmaDescriptor from autospi (#3717)
38611e9dd7 [hal] Fix REVPH Analog Pressure channel selection (#3716)
4d78def31e [wpilib] Add DeadbandElimination forwarding to PWMMotorController (#3714)
3be0c1217a [wpilibcExamples] Make GearsBot use idiomatic C++ (#3711)
42d3a50aa2 [hal] Check error codes during serial port initialization (#3712)
52f1464029 Add project with field images and their json config files (#3668)
68ce62e2e9 [hal] Add autodetect for power modules (#3706)
3dd41c0d37 [wpilib] Don't print PD errors for LiveWindow reads (#3708)
7699a1f827 [hal] Fix sim not working with automatic PD type and default module (#3707)

Change-Id: I6b0b8fa8b2d2a24071240f624db9ec6d127f6648
git-subtree-dir: third_party/allwpilib
git-subtree-split: e4b91005cf69161f1cb3d934f6526232e6b9169e
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/wpiutil/src/main/native/cpp/HttpUtil.cpp b/wpiutil/src/main/native/cpp/HttpUtil.cpp
index afce3a6..b8b7cc8 100644
--- a/wpiutil/src/main/native/cpp/HttpUtil.cpp
+++ b/wpiutil/src/main/native/cpp/HttpUtil.cpp
@@ -232,7 +232,7 @@
 
     // Did we find the boundary?
     if (searchBuf[0] == '-' && searchBuf[1] == '-' &&
-        searchBuf.substr(2) == boundary) {
+        wpi::substr(searchBuf, 2) == boundary) {
       return true;
     }
 
diff --git a/wpiutil/src/main/native/cpp/MimeTypes.cpp b/wpiutil/src/main/native/cpp/MimeTypes.cpp
index 082dab1..5f5bf59 100644
--- a/wpiutil/src/main/native/cpp/MimeTypes.cpp
+++ b/wpiutil/src/main/native/cpp/MimeTypes.cpp
@@ -4,6 +4,7 @@
 
 #include "wpi/MimeTypes.h"
 
+#include "wpi/StringExtras.h"
 #include "wpi/StringMap.h"
 
 namespace wpi {
@@ -53,11 +54,11 @@
 
   auto pos = path.find_last_of("/");
   if (pos != std::string_view::npos) {
-    path = path.substr(pos + 1);
+    path = wpi::substr(path, pos + 1);
   }
   auto dot_pos = path.find_last_of(".");
   if (dot_pos > 0 && dot_pos != std::string_view::npos) {
-    auto type = mimeTypes.find(path.substr(dot_pos + 1));
+    auto type = mimeTypes.find(wpi::substr(path, dot_pos + 1));
     if (type != mimeTypes.end()) {
       return type->getValue();
     }
diff --git a/wpiutil/src/main/native/cpp/MulticastHandleManager.cpp b/wpiutil/src/main/native/cpp/MulticastHandleManager.cpp
new file mode 100644
index 0000000..d249a1c
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/MulticastHandleManager.cpp
@@ -0,0 +1,12 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "MulticastHandleManager.h"
+
+using namespace wpi;
+
+MulticastHandleManager& wpi::GetMulticastManager() {
+  static MulticastHandleManager manager;
+  return manager;
+}
diff --git a/wpiutil/src/main/native/cpp/MulticastHandleManager.h b/wpiutil/src/main/native/cpp/MulticastHandleManager.h
new file mode 100644
index 0000000..be8d061
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/MulticastHandleManager.h
@@ -0,0 +1,25 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#pragma once
+
+#include <memory>
+
+#include "wpi/DenseMap.h"
+#include "wpi/MulticastServiceAnnouncer.h"
+#include "wpi/MulticastServiceResolver.h"
+#include "wpi/UidVector.h"
+
+namespace wpi {
+struct MulticastHandleManager {
+  wpi::mutex mutex;
+  wpi::UidVector<int, 8> handleIds;
+  wpi::DenseMap<size_t, std::unique_ptr<wpi::MulticastServiceResolver>>
+      resolvers;
+  wpi::DenseMap<size_t, std::unique_ptr<wpi::MulticastServiceAnnouncer>>
+      announcers;
+};
+
+MulticastHandleManager& GetMulticastManager();
+}  // namespace wpi
diff --git a/wpiutil/src/main/native/cpp/MulticastServiceAnnouncer.cpp b/wpiutil/src/main/native/cpp/MulticastServiceAnnouncer.cpp
new file mode 100644
index 0000000..736a03d
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/MulticastServiceAnnouncer.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpi/MulticastServiceAnnouncer.h"
+
+#include <wpi/SmallVector.h>
+
+#include "MulticastHandleManager.h"
+
+extern "C" {
+WPI_MulticastServiceAnnouncerHandle WPI_CreateMulticastServiceAnnouncer(
+    const char* serviceName, const char* serviceType, int32_t port,
+    int32_t txtCount, const char** keys, const char** values)
+
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+
+  wpi::SmallVector<std::pair<std::string_view, std::string_view>, 8> txts;
+
+  for (int32_t i = 0; i < txtCount; i++) {
+    txts.emplace_back(
+        std::pair<std::string_view, std::string_view>{keys[i], values[i]});
+  }
+
+  auto announcer = std::make_unique<wpi::MulticastServiceAnnouncer>(
+      serviceName, serviceType, port, txts);
+
+  size_t index = manager.handleIds.emplace_back(3);
+  manager.announcers[index] = std::move(announcer);
+
+  return index;
+}
+
+void WPI_FreeMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.announcers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+void WPI_StartMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Start();
+}
+
+void WPI_StopMulticastServiceAnnouncer(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Stop();
+}
+
+int32_t WPI_GetMulticastServiceAnnouncerHasImplementation(
+    WPI_MulticastServiceAnnouncerHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  return announcer->HasImplementation();
+}
+}  // extern "C"
diff --git a/wpiutil/src/main/native/cpp/MulticastServiceResolver.cpp b/wpiutil/src/main/native/cpp/MulticastServiceResolver.cpp
new file mode 100644
index 0000000..b834f17
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/MulticastServiceResolver.cpp
@@ -0,0 +1,148 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "wpi/MulticastServiceResolver.h"
+
+#include "MulticastHandleManager.h"
+#include "wpi/MemAlloc.h"
+
+extern "C" {
+WPI_MulticastServiceResolverHandle WPI_CreateMulticastServiceResolver(
+    const char* serviceType)
+
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+
+  auto resolver = std::make_unique<wpi::MulticastServiceResolver>(serviceType);
+
+  size_t index = manager.handleIds.emplace_back(2);
+  manager.resolvers[index] = std::move(resolver);
+
+  return index;
+}
+
+void WPI_FreeMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.resolvers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+void WPI_StartMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Start();
+}
+
+void WPI_StopMulticastServiceResolver(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Stop();
+}
+
+int32_t WPI_GetMulticastServiceResolverHasImplementation(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->HasImplementation();
+}
+
+WPI_EventHandle WPI_GetMulticastServiceResolverEventHandle(
+    WPI_MulticastServiceResolverHandle handle) {
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->GetEventHandle();
+}
+
+WPI_ServiceData* WPI_GetMulticastServiceResolverData(
+    WPI_MulticastServiceResolverHandle handle, int32_t* dataCount) {
+  std::vector<wpi::MulticastServiceResolver::ServiceData> allData;
+  {
+    auto& manager = wpi::GetMulticastManager();
+    std::scoped_lock lock{manager.mutex};
+    auto& resolver = manager.resolvers[handle];
+    allData = resolver->GetData();
+  }
+  if (allData.empty()) {
+    *dataCount = 0;
+    return nullptr;
+  }
+  size_t allocSize = sizeof(WPI_ServiceData) * allData.size();
+
+  for (auto&& data : allData) {
+    // Include space for hostName and serviceType (+ terminators)
+    allocSize += data.hostName.size() + data.serviceName.size() + 2;
+
+    size_t keysTotalLength = 0;
+    size_t valuesTotalLength = 0;
+    // Include space for all keys and values, and pointer array
+    for (auto&& t : data.txt) {
+      allocSize += sizeof(const char*);
+      keysTotalLength += (t.first.size() + 1);
+      allocSize += sizeof(const char*);
+      valuesTotalLength += (t.second.size() + 1);
+    }
+    allocSize += keysTotalLength;
+    allocSize += valuesTotalLength;
+  }
+
+  uint8_t* cDataRaw = reinterpret_cast<uint8_t*>(wpi::safe_malloc(allocSize));
+  if (!cDataRaw) {
+    return nullptr;
+  }
+  WPI_ServiceData* rootArray = reinterpret_cast<WPI_ServiceData*>(cDataRaw);
+  cDataRaw += (sizeof(WPI_ServiceData) + allData.size());
+  WPI_ServiceData* currentData = rootArray;
+
+  for (auto&& data : allData) {
+    currentData->ipv4Address = data.ipv4Address;
+    currentData->port = data.port;
+    currentData->txtCount = data.txt.size();
+
+    std::memcpy(cDataRaw, data.hostName.c_str(), data.hostName.size() + 1);
+    currentData->hostName = reinterpret_cast<const char*>(cDataRaw);
+    cDataRaw += data.hostName.size() + 1;
+
+    std::memcpy(cDataRaw, data.serviceName.c_str(),
+                data.serviceName.size() + 1);
+    currentData->serviceName = reinterpret_cast<const char*>(cDataRaw);
+    cDataRaw += data.serviceName.size() + 1;
+
+    char** valuesPtrArr = reinterpret_cast<char**>(cDataRaw);
+    cDataRaw += (sizeof(char**) * data.txt.size());
+    char** keysPtrArr = reinterpret_cast<char**>(cDataRaw);
+    cDataRaw += (sizeof(char**) * data.txt.size());
+
+    currentData->txtKeys = const_cast<const char**>(keysPtrArr);
+    currentData->txtValues = const_cast<const char**>(valuesPtrArr);
+
+    for (size_t i = 0; i < data.txt.size(); i++) {
+      keysPtrArr[i] = reinterpret_cast<char*>(cDataRaw);
+      std::memcpy(keysPtrArr[i], data.txt[i].first.c_str(),
+                  data.txt[i].first.size() + 1);
+      cDataRaw += (data.txt[i].first.size() + 1);
+
+      valuesPtrArr[i] = reinterpret_cast<char*>(cDataRaw);
+      std::memcpy(valuesPtrArr[i], data.txt[i].second.c_str(),
+                  data.txt[i].second.size() + 1);
+      cDataRaw += (data.txt[i].second.size() + 1);
+    }
+    currentData++;
+  }
+
+  return rootArray;
+}
+
+void WPI_FreeServiceData(WPI_ServiceData* serviceData, int32_t length) {
+  std::free(serviceData);
+}
+}  // extern "C"
diff --git a/wpiutil/src/main/native/cpp/StackTraceWrap.cpp b/wpiutil/src/main/native/cpp/StackTraceWrap.cpp
new file mode 100644
index 0000000..37a83f7
--- /dev/null
+++ b/wpiutil/src/main/native/cpp/StackTraceWrap.cpp
@@ -0,0 +1,18 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include <atomic>
+
+#include "wpi/StackTrace.h"
+
+static std::atomic<std::string (*)(int offset)> gStackTraceImpl{
+    wpi::GetStackTraceDefault};
+
+std::string wpi::GetStackTrace(int offset) {
+  return (gStackTraceImpl.load())(offset);
+}
+
+void wpi::SetGetStackTraceImpl(std::string (*func)(int offset)) {
+  gStackTraceImpl = func ? func : wpi::GetStackTraceDefault;
+}
diff --git a/wpiutil/src/main/native/cpp/StringExtras.cpp b/wpiutil/src/main/native/cpp/StringExtras.cpp
index 45d23ea..968ffc3 100644
--- a/wpiutil/src/main/native/cpp/StringExtras.cpp
+++ b/wpiutil/src/main/native/cpp/StringExtras.cpp
@@ -65,7 +65,7 @@
 std::string_view::size_type wpi::find_lower(
     std::string_view str, std::string_view other,
     std::string_view::size_type from) noexcept {
-  auto s = str.substr(from);
+  auto s = substr(str, from);
   while (s.size() >= other.size()) {
     if (starts_with_lower(s, other)) {
       return from;
@@ -98,7 +98,7 @@
   }
   for (size_t i = str.size() - n + 1, e = 0; i != e;) {
     --i;
-    if (equals_lower(str.substr(i, n), other)) {
+    if (equals_lower(substr(str, i, n), other)) {
       return i;
     }
   }
diff --git a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
index 4fc5f45..e876d9c 100644
--- a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
+++ b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp
@@ -4,9 +4,14 @@
 
 #include <jni.h>
 
+#include "../MulticastHandleManager.h"
 #include "edu_wpi_first_util_WPIUtilJNI.h"
+#include "wpi/DenseMap.h"
+#include "wpi/MulticastServiceAnnouncer.h"
+#include "wpi/MulticastServiceResolver.h"
 #include "wpi/PortForwarder.h"
 #include "wpi/Synchronization.h"
+#include "wpi/UidVector.h"
 #include "wpi/jni_util.h"
 #include "wpi/timestamp.h"
 
@@ -16,6 +21,8 @@
 static uint64_t mockNow = 0;
 
 static JException interruptedEx;
+static JClass serviceDataCls;
+static JGlobal<jobjectArray> serviceDataEmptyArray;
 
 extern "C" {
 
@@ -30,6 +37,17 @@
     return JNI_ERR;
   }
 
+  serviceDataCls = JClass{env, "edu/wpi/first/util/ServiceData"};
+  if (!serviceDataCls) {
+    return JNI_ERR;
+  }
+
+  serviceDataEmptyArray = JGlobal<jobjectArray>{
+      env, env->NewObjectArray(0, serviceDataCls, nullptr)};
+  if (serviceDataEmptyArray == nullptr) {
+    return JNI_ERR;
+  }
+
   return JNI_VERSION_1_6;
 }
 
@@ -38,6 +56,9 @@
   if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
     return;
   }
+
+  serviceDataEmptyArray.free(env);
+  serviceDataCls.free(env);
   interruptedEx.free(env);
 }
 
@@ -56,6 +77,19 @@
 
 /*
  * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    disableMockTime
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_disableMockTime
+  (JNIEnv*, jclass)
+{
+  mockTimeEnabled = false;
+  wpi::SetNowImpl(nullptr);
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
  * Method:    setMockTime
  * Signature: (J)V
  */
@@ -84,6 +118,18 @@
 
 /*
  * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    getSystemTime
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_getSystemTime
+  (JNIEnv*, jclass)
+{
+  return wpi::GetSystemTime();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
  * Method:    addPortForwarder
  * Signature: (ILjava/lang/String;I)V
  */
@@ -273,4 +319,257 @@
   return MakeJIntArray(env, signaled);
 }
 
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    createMulticastServiceAnnouncer
+ * Signature: (Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/Object;[Ljava/lang/Object;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_createMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jstring serviceName, jstring serviceType, jint port,
+   jobjectArray keys, jobjectArray values)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+
+  JStringRef serviceNameRef{env, serviceName};
+  JStringRef serviceTypeRef{env, serviceType};
+
+  size_t keysLen = env->GetArrayLength(keys);
+  wpi::SmallVector<std::pair<std::string, std::string>, 8> txtVec;
+  txtVec.reserve(keysLen);
+  for (size_t i = 0; i < keysLen; i++) {
+    JLocal<jstring> key{
+        env, static_cast<jstring>(env->GetObjectArrayElement(keys, i))};
+    JLocal<jstring> value{
+        env, static_cast<jstring>(env->GetObjectArrayElement(values, i))};
+
+    txtVec.emplace_back(std::pair<std::string, std::string>{
+        JStringRef{env, key}.str(), JStringRef{env, value}.str()});
+  }
+
+  auto announcer = std::make_unique<wpi::MulticastServiceAnnouncer>(
+      serviceNameRef.str(), serviceTypeRef.str(), port, txtVec);
+
+  size_t index = manager.handleIds.emplace_back(1);
+
+  manager.announcers[index] = std::move(announcer);
+
+  return static_cast<jint>(index);
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    freeMulticastServiceAnnouncer
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_freeMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.announcers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    startMulticastServiceAnnouncer
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_startMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Start();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    stopMulticastServiceAnnouncer
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_stopMulticastServiceAnnouncer
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  announcer->Stop();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    getMulticastServiceAnnouncerHasImplementation
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceAnnouncerHasImplementation
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& announcer = manager.announcers[handle];
+  return announcer->HasImplementation();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    createMulticastServiceResolver
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_createMulticastServiceResolver
+  (JNIEnv* env, jclass, jstring serviceType)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  JStringRef serviceTypeRef{env, serviceType};
+
+  auto resolver =
+      std::make_unique<wpi::MulticastServiceResolver>(serviceTypeRef.str());
+
+  size_t index = manager.handleIds.emplace_back(2);
+
+  manager.resolvers[index] = std::move(resolver);
+
+  return static_cast<jint>(index);
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    freeMulticastServiceResolver
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_freeMulticastServiceResolver
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  manager.resolvers[handle] = nullptr;
+  manager.handleIds.erase(handle);
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    startMulticastServiceResolver
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_startMulticastServiceResolver
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Start();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    stopMulticastServiceResolver
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_stopMulticastServiceResolver
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  resolver->Stop();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    getMulticastServiceResolverHasImplementation
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceResolverHasImplementation
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->HasImplementation();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    getMulticastServiceResolverEventHandle
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceResolverEventHandle
+  (JNIEnv* env, jclass, jint handle)
+{
+  auto& manager = wpi::GetMulticastManager();
+  std::scoped_lock lock{manager.mutex};
+  auto& resolver = manager.resolvers[handle];
+  return resolver->GetEventHandle();
+}
+
+/*
+ * Class:     edu_wpi_first_util_WPIUtilJNI
+ * Method:    getMulticastServiceResolverData
+ * Signature: (I)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_edu_wpi_first_util_WPIUtilJNI_getMulticastServiceResolverData
+  (JNIEnv* env, jclass, jint handle)
+{
+  static jmethodID constructor =
+      env->GetMethodID(serviceDataCls, "<init>",
+                       "(JILjava/lang/String;Ljava/lang/String;[Ljava/lang/"
+                       "String;[Ljava/lang/String;)V");
+  auto& manager = wpi::GetMulticastManager();
+  std::vector<wpi::MulticastServiceResolver::ServiceData> allData;
+  {
+    std::scoped_lock lock{manager.mutex};
+    auto& resolver = manager.resolvers[handle];
+    allData = resolver->GetData();
+  }
+  if (allData.empty()) {
+    return serviceDataEmptyArray;
+  }
+
+  JLocal<jobjectArray> returnData{
+      env, env->NewObjectArray(allData.size(), serviceDataCls, nullptr)};
+
+  for (auto&& data : allData) {
+    JLocal<jstring> serviceName{env, MakeJString(env, data.serviceName)};
+    JLocal<jstring> hostName{env, MakeJString(env, data.hostName)};
+
+    wpi::SmallVector<std::string_view, 8> keysRef;
+    wpi::SmallVector<std::string_view, 8> valuesRef;
+
+    size_t index = 0;
+    for (auto&& txt : data.txt) {
+      keysRef.emplace_back(txt.first);
+      valuesRef.emplace_back(txt.second);
+    }
+
+    JLocal<jobjectArray> keys{env, MakeJStringArray(env, keysRef)};
+    JLocal<jobjectArray> values{env, MakeJStringArray(env, valuesRef)};
+
+    JLocal<jobject> dataItem{
+        env, env->NewObject(serviceDataCls, constructor,
+                            static_cast<jlong>(data.ipv4Address),
+                            static_cast<jint>(data.port), serviceName.obj(),
+                            hostName.obj(), keys.obj(), values.obj())};
+
+    env->SetObjectArrayElement(returnData, index, dataItem);
+    index++;
+  }
+
+  return returnData;
+}
+
 }  // extern "C"
diff --git a/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp b/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
index 3ad39e5..2c591e9 100644
--- a/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
+++ b/wpiutil/src/main/native/cpp/sendable/SendableRegistry.cpp
@@ -60,11 +60,24 @@
   return *components[compUid - 1];
 }
 
-static SendableRegistryInst& GetInstance() {
-  static SendableRegistryInst instance;
+static std::unique_ptr<SendableRegistryInst>& GetInstanceHolder() {
+  static std::unique_ptr<SendableRegistryInst> instance =
+      std::make_unique<SendableRegistryInst>();
   return instance;
 }
 
+static SendableRegistryInst& GetInstance() {
+  return *GetInstanceHolder();
+}
+
+#ifndef __FRC_ROBORIO__
+namespace wpi::impl {
+void ResetSendableRegistry() {
+  std::make_unique<SendableRegistryInst>().swap(GetInstanceHolder());
+}
+}  // namespace wpi::impl
+#endif
+
 void SendableRegistry::SetLiveWindowBuilderFactory(
     std::function<std::unique_ptr<SendableBuilder>()> factory) {
   GetInstance().liveWindowFactory = std::move(factory);
diff --git a/wpiutil/src/main/native/cpp/timestamp.cpp b/wpiutil/src/main/native/cpp/timestamp.cpp
index 9c32bf3..521197f 100644
--- a/wpiutil/src/main/native/cpp/timestamp.cpp
+++ b/wpiutil/src/main/native/cpp/timestamp.cpp
@@ -16,7 +16,7 @@
 #endif
 
 // offset in microseconds
-static uint64_t zerotime() noexcept {
+static uint64_t time_since_epoch() noexcept {
 #ifdef _WIN32
   FILETIME ft;
   uint64_t tmpres = 0;
@@ -35,7 +35,7 @@
 #else
   // 1-us intervals
   return std::chrono::duration_cast<std::chrono::microseconds>(
-             std::chrono::high_resolution_clock::now().time_since_epoch())
+             std::chrono::system_clock::now().time_since_epoch())
       .count();
 #endif
 }
@@ -66,7 +66,7 @@
 }
 #endif
 
-static const uint64_t zerotime_val = zerotime();
+static const uint64_t zerotime_val = time_since_epoch();
 static const uint64_t offset_val = timestamp();
 #ifdef _WIN32
 static const uint64_t frequency_val = update_frequency();
@@ -96,6 +96,10 @@
   return (now_impl.load())();
 }
 
+uint64_t wpi::GetSystemTime() {
+  return time_since_epoch();
+}
+
 extern "C" {
 
 uint64_t WPI_NowDefault(void) {
@@ -110,4 +114,8 @@
   return wpi::Now();
 }
 
+uint64_t WPI_GetSystemTime(void) {
+  return wpi::GetSystemTime();
+}
+
 }  // extern "C"